mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
some adjustments for dynamic maps
This commit is contained in:
parent
e78784b337
commit
5bbd4714b6
@ -16,7 +16,7 @@ import { LeafletRefsProvider } from '../Map/hooks/useLeafletRefs'
|
||||
import { SelectPositionProvider } from '../Map/hooks/useSelectPosition'
|
||||
import { ClusterRefProvider } from '../Map/hooks/useClusterRef'
|
||||
|
||||
export function AppShell({ appName, nameWidth, children, assetsApi }: { appName: string, nameWidth?: number, children: React.ReactNode, assetsApi: AssetsApi }) {
|
||||
export function AppShell({ appName, children, assetsApi }: { appName: string, children: React.ReactNode, assetsApi: AssetsApi }) {
|
||||
|
||||
// Create a client
|
||||
const queryClient = new QueryClient()
|
||||
@ -46,7 +46,7 @@ export function AppShell({ appName, nameWidth, children, assetsApi }: { appName:
|
||||
draggable
|
||||
pauseOnHover
|
||||
theme="light" />
|
||||
<NavBar appName={appName} nameWidth={nameWidth}></NavBar>
|
||||
<NavBar appName={appName}></NavBar>
|
||||
<div id="app-content" className="tw-flex tw-!pl-[77px]">
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@ -2,12 +2,12 @@ import { useAuth } from "../Auth"
|
||||
import { Link } from "react-router-dom";
|
||||
import { toast } from "react-toastify";
|
||||
import QuestionMarkIcon from '@heroicons/react/24/outline/QuestionMarkCircleIcon'
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useItems } from "../Map/hooks/useItems";
|
||||
import { Item } from "../../types";
|
||||
|
||||
|
||||
export default function NavBar({ appName, nameWidth = 200}: { appName: string, nameWidth?: number }) {
|
||||
export default function NavBar({ appName}: { appName: string }) {
|
||||
|
||||
|
||||
const { isAuthenticated, user, logout } = useAuth();
|
||||
@ -16,15 +16,21 @@ export default function NavBar({ appName, nameWidth = 200}: { appName: string, n
|
||||
const items = useItems();
|
||||
|
||||
useEffect(() => {
|
||||
const profile = user && items.find(i => i.user_created.id === user.id && i.type === "user");
|
||||
const profile = user && items.find(i => (i.user_created.id === user.id) && i.layer?.itemType.name === "user");
|
||||
profile ? setUserProfile(profile) : setUserProfile({id: crypto.randomUUID(), name: user?.first_name, text: ""});
|
||||
|
||||
}, [user, items])
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
}, [userProfile])
|
||||
|
||||
const nameRef = useRef<any>(null);
|
||||
const [nameWidth, setNameWidth] = useState<number>(0);
|
||||
|
||||
useEffect(() => {
|
||||
nameRef && setNameWidth(nameRef.current.scrollWidth)
|
||||
}, [nameRef, appName])
|
||||
|
||||
|
||||
|
||||
const onLogout = () => {
|
||||
@ -58,8 +64,8 @@ export default function NavBar({ appName, nameWidth = 200}: { appName: string, n
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" className="tw-inline-block tw-w-5 tw-h-5 tw-stroke-current"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h16M4 18h16"></path></svg>
|
||||
</button>
|
||||
<div className="tw-flex-1 tw-mr-2">
|
||||
<div className={`tw-flex-1 tw-truncate tw-grid tw-grid-flow-col`} style={{maxWidth: nameWidth}}>
|
||||
<Link className="tw-btn tw-btn-ghost tw-px-2 tw-normal-case tw-text-xl tw-flex-1 tw-truncate" to={"/"}><h1 className="tw-truncate">{appName}</h1></Link>
|
||||
<div className={`tw-flex-1 tw-truncate tw-grid tw-grid-flow-col`} style={{maxWidth: nameWidth+60}}>
|
||||
<Link className="tw-btn tw-btn-ghost tw-px-2 tw-normal-case tw-text-xl tw-flex-1 tw-truncate" to={"/"}><h1 ref={nameRef} className="tw-truncate">{appName}</h1></Link>
|
||||
<button className="tw-btn tw-px-2 tw-btn-ghost" onClick={() => window.my_modal_3.showModal()}><QuestionMarkIcon className="tw-h-5 tw-w-5" /></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -20,7 +20,7 @@ export function Quests() {
|
||||
const items = useItems();
|
||||
|
||||
useEffect(() => {
|
||||
setProfie(items.find(i => i.user_created.id === user?.id && i.type =="user"))
|
||||
setProfie(items.find(i => i.user_created?.id === user?.id && i.layer?.itemType.name == "user" && i.user_created.id != null))
|
||||
}, [items, user])
|
||||
|
||||
|
||||
|
||||
@ -84,9 +84,9 @@ export const Layer = ({
|
||||
useMapEvents({
|
||||
popupopen: (e) => {
|
||||
const item = Object.entries(leafletRefs).find(r => r[1].popup == e.popup)?.[1].item;
|
||||
if (item?.layer?.name == name && window.location.pathname.split("/")[2] != item.id) {
|
||||
if (item?.layer?.name == name && window.location.pathname.split("/")[1] != item.id) {
|
||||
let params = new URLSearchParams(window.location.search);
|
||||
window.history.pushState({}, "", `/${name}/${item.id}` + `${params.toString() !== "" ? `?${params}` : ""}`)
|
||||
window.history.pushState({}, "", `/${item.id}` + `${params.toString() !== "" ? `?${params}` : ""}`)
|
||||
let title = "";
|
||||
if (item.name) title = item.name;
|
||||
else if (item.layer?.itemNameField) title = getValue(item, item.layer.itemNameField);
|
||||
@ -96,26 +96,23 @@ export const Layer = ({
|
||||
})
|
||||
|
||||
const openPopup = () => {
|
||||
if (window.location.pathname.split("/").length <= 2 || window.location.pathname.split("/")[2] === "") {
|
||||
if (window.location.pathname.split("/").length <= 1 || window.location.pathname.split("/")[1] === "") {
|
||||
map.closePopup();
|
||||
}
|
||||
else {
|
||||
if (window.location.pathname.split("/")[1] == name) {
|
||||
if (window.location.pathname.split("/")[2]) {
|
||||
const id = window.location.pathname.split("/")[2]
|
||||
const marker = leafletRefs[id]?.marker;
|
||||
if (marker) {
|
||||
marker && clusterRef.hasLayer(marker) && clusterRef?.zoomToShowLayer(marker, () => {
|
||||
marker.openPopup();
|
||||
});
|
||||
const item = leafletRefs[id]?.item;
|
||||
let title = "";
|
||||
if (item.name) title = item.name;
|
||||
else if (item.layer?.itemNameField) title = getValue(item, item.layer.itemNameField);
|
||||
document.title = `${document.title.split("-")[0]} - ${title}`;
|
||||
document.querySelector('meta[property="og:title"]')?.setAttribute("content", item.name);
|
||||
document.querySelector('meta[property="og:description"]')?.setAttribute("content", item.text);
|
||||
}
|
||||
if (window.location.pathname.split("/")[1]) {
|
||||
const id = window.location.pathname.split("/")[1]
|
||||
const ref = leafletRefs[id];
|
||||
if (ref?.marker && ref.item.layer?.name === name) {
|
||||
ref.marker && clusterRef.hasLayer(ref.marker) && clusterRef?.zoomToShowLayer(ref.marker, () => {
|
||||
ref.marker.openPopup();
|
||||
});
|
||||
let title = "";
|
||||
if (ref.item.name) title = ref.item.name;
|
||||
else if (ref.item.layer?.itemNameField) title = getValue(ref.item.name, ref.item.layer.itemNameField);
|
||||
document.title = `${document.title.split("-")[0]} - ${title}`;
|
||||
document.querySelector('meta[property="og:title"]')?.setAttribute("content", ref.item.name);
|
||||
document.querySelector('meta[property="og:description"]')?.setAttribute("content", ref.item.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,9 +34,9 @@ export default function AddButton({ triggerAction }: { triggerAction: React.Disp
|
||||
<div className="tw-tooltip tw-tooltip-left" data-tip={layer.menuText}>
|
||||
<button tabIndex={0}
|
||||
className="tw-z-500 tw-border-0 tw-pl-2 tw-p-0 tw-mb-3 tw-w-10 tw-h-10 tw-cursor-pointer tw-rounded-full tw-mouse tw-drop-shadow-md tw-transition tw-ease-in tw-duration-200 focus:tw-outline-none"
|
||||
style={{ backgroundColor: layer.menuColor }}
|
||||
style={{ backgroundColor: layer.menuColor || "#777"}}
|
||||
onClick={() => { triggerAction(layer) }}>
|
||||
<layer.menuIcon className="tw-h-6 tw-w-6 tw-text-white" ></layer.menuIcon>
|
||||
<img src={layer.menuIcon} className="tw-h-6 tw-w-6 tw-text-white" style={{ filter: 'invert(100%) brightness(200%)' }} />
|
||||
</button>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
@ -82,7 +82,9 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
|
||||
map.closePopup();
|
||||
}
|
||||
else {
|
||||
const item = items.find(i => i.user_created.id === user?.id && i.type === props.layer.itemType)
|
||||
const item = items.find(i => i.user_created.id === user?.id && i.layer?.itemType.name === props.layer.itemType.name);
|
||||
console.log(item);
|
||||
|
||||
const uuid = crypto.randomUUID();
|
||||
let success = false;
|
||||
try {
|
||||
@ -92,9 +94,7 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
|
||||
} catch (error) {
|
||||
toast.error(error.toString());
|
||||
}
|
||||
if(success) {
|
||||
console.log(props.layer);
|
||||
|
||||
if(success) {
|
||||
props.layer.onlyOnePerOwner && item && updateItem({...item, ...formItem});
|
||||
(!props.layer.onlyOnePerOwner || !item) && addItem({...formItem, name: formItem.name ? formItem.name : user?.first_name , user_created: user, type: props.layer.itemType, id: uuid, layer: props.layer});
|
||||
toast.success("New item created");
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { useHasUserPermission, usePermissions } from "../Map/hooks/usePermissions";
|
||||
import DialogModal from "../Templates/DialogModal";
|
||||
import { useItems } from "../Map/hooks/useItems";
|
||||
import { TextView } from "../Map";
|
||||
import { HeaderView } from "../Map/Subcomponents/ItemPopupComponents/HeaderView";
|
||||
import { Item } from "../../types";
|
||||
|
||||
@ -20,7 +19,7 @@ export function ActionButton({ item, triggerAddButton, triggerItemSelected, exis
|
||||
|
||||
const items = useItems();
|
||||
|
||||
const filterdItems = items.filter(i => i.type == itemType).filter(i => !existingRelations.some(s => s.id == i.id)).filter(i => i.id != item.id)
|
||||
const filterdItems = items.filter(i => i.layer?.itemType.name == itemType).filter(i => !existingRelations.some(s => s.id == i.id)).filter(i => i.id != item.id)
|
||||
|
||||
|
||||
|
||||
|
||||
@ -276,7 +276,7 @@ export function OverlayItemProfile() {
|
||||
<div className='tw-h-full'>
|
||||
<div className='tw-grid tw-grid-cols-1 sm:tw-grid-cols-2 md:tw-grid-cols-1 lg:tw-grid-cols-1 xl:tw-grid-cols-1 2xl:tw-grid-cols-2 tw-pb-5'>
|
||||
{relations && relations.map(i => {
|
||||
if (i.type == 'project') return (
|
||||
if (i.layer?.itemType.name == 'project') return (
|
||||
|
||||
<div key={i.id} className='tw-cursor-pointer tw-card tw-border-[1px] tw-border-base-300 tw-card-body tw-shadow-xl tw-bg-base-100 tw-text-base-content tw-mx-4 tw-p-4 tw-mb-4 tw-h-fit' onClick={() => navigate('/item/' + i.id)}>
|
||||
<LinkedItemsHeaderView unlinkPermission={updatePermission} loading={loading} item={i} unlinkCallback={unlinkItem} />
|
||||
@ -316,7 +316,7 @@ export function OverlayItemProfile() {
|
||||
<div className='tw-h-full'>
|
||||
<div className='tw-grid tw-grid-cols-1 sm:tw-grid-cols-2 md:tw-grid-cols-1 lg:tw-grid-cols-1 xl:tw-grid-cols-1 2xl:tw-grid-cols-2'>
|
||||
{relations && relations.map(i => {
|
||||
if (i.type == 'event') return (
|
||||
if (i.layer?.itemType.name == 'event') return (
|
||||
|
||||
<div key={i.id} className='tw-cursor-pointer tw-card tw-border-[1px] tw-border-base-300 tw-card-body tw-shadow-xl tw-bg-base-100 tw-text-base-content tw-mx-4 tw-p-6 tw-mb-4' onClick={() => navigate('/item/' + i.id)}>
|
||||
<LinkedItemsHeaderView unlinkPermission={updatePermission} item={i} unlinkCallback={unlinkItem} loading={loading} />
|
||||
@ -332,7 +332,7 @@ export function OverlayItemProfile() {
|
||||
{addItemPopupType == "event" ?
|
||||
<form autoComplete='off' onSubmit={e => submitNewItem(e, addItemPopupType)} >
|
||||
|
||||
<div className='tw-cursor-pointer tw-card tw-border-[1px] tw-border-base-300 tw-card-body tw-shadow-xl tw-bg-base-100 tw-text-base-content tw-mx-4 tw-p-4 tw-mb-4'>
|
||||
<div className='tw-cursor-pointer tw-card tw-border-[1px] tw-border-base-300 tw-card-body tw-shadow-xl tw-bg-base-100 tw-text-base-content tw-mx-4 tw-p-6 tw-mb-4'>
|
||||
<label className="tw-btn tw-btn-sm tw-rounded-2xl tw-btn-circle tw-btn-ghost hover:tw-bg-transparent tw-absolute tw-right-0 tw-top-0 tw-text-gray-600" onClick={() => {
|
||||
setAddItemPopupType("")
|
||||
}}>
|
||||
@ -346,7 +346,7 @@ export function OverlayItemProfile() {
|
||||
</div>
|
||||
</form> : <></>
|
||||
}
|
||||
{updatePermission && <ActionButton collection="items" item={item} existingRelations={relations} itemType={"event"} triggerItemSelected={linkItem} triggerAddButton={() => { setAddItemPopupType("user"); scroll() }} color={item.color}></ActionButton>}
|
||||
{updatePermission && <ActionButton collection="items" item={item} existingRelations={relations} itemType={"event"} triggerItemSelected={linkItem} triggerAddButton={() => { setAddItemPopupType("event"); scroll() }} color={item.color}></ActionButton>}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@ -356,7 +356,7 @@ export function OverlayItemProfile() {
|
||||
<div className='tw-h-full'>
|
||||
<div className='tw-grid tw-grid-cols-1 sm:tw-grid-cols-2 md:tw-grid-cols-1 lg:tw-grid-cols-1 xl:tw-grid-cols-1 2xl:tw-grid-cols-2'>
|
||||
{relations && relations.map(i => {
|
||||
if (i.type == 'user') return (
|
||||
if (i.layer?.itemType.name == 'user') return (
|
||||
|
||||
<div key={i.id} className='tw-cursor-pointer tw-card tw-border-[1px] tw-border-base-300 tw-card-body tw-shadow-xl tw-bg-base-100 tw-text-base-content tw-mx-4 tw-p-6 tw-mb-4' onClick={() => navigate('/item/' + i.id)}>
|
||||
<LinkedItemsHeaderView unlinkPermission={updatePermission} item={i} unlinkCallback={unlinkItem} loading={loading} />
|
||||
|
||||
@ -22,7 +22,7 @@ type breadcrumb = {
|
||||
}
|
||||
|
||||
|
||||
export const OverlayItemsIndexPage = ({url, type, parameterField, breadcrumbs, itemNameField, itemTextField, itemImageField, itemSymbolField, itemSubnameField, plusButton = true, children }: { type: string, url: string, parameterField: string, breadcrumbs: Array<breadcrumb>, itemNameField: string, itemTextField: string, itemImageField: string, itemSymbolField: string, itemSubnameField: string, plusButton?: boolean, children?: ReactNode }) => {
|
||||
export const OverlayItemsIndexPage = ({url, layerName, parameterField, breadcrumbs, itemNameField, itemTextField, itemImageField, itemSymbolField, itemSubnameField, plusButton = true, children }: { layerName: string, url: string, parameterField: string, breadcrumbs: Array<breadcrumb>, itemNameField: string, itemTextField: string, itemImageField: string, itemSymbolField: string, itemSubnameField: string, plusButton?: boolean, children?: ReactNode }) => {
|
||||
|
||||
console.log(itemSymbolField);
|
||||
|
||||
@ -56,10 +56,9 @@ export const OverlayItemsIndexPage = ({url, type, parameterField, breadcrumbs, i
|
||||
|
||||
}, [items])
|
||||
|
||||
const layer = layers.find(l => l.name == layerName);
|
||||
|
||||
|
||||
const layer = layers.find(l => l.itemType == type);
|
||||
|
||||
const submitNewItem = async (evt: any) => {
|
||||
evt.preventDefault();
|
||||
const formItem: Item = {} as Item;
|
||||
@ -77,7 +76,7 @@ export const OverlayItemsIndexPage = ({url, type, parameterField, breadcrumbs, i
|
||||
const uuid = crypto.randomUUID();
|
||||
let success = false;
|
||||
try {
|
||||
await layer?.api?.createItem!({ ...formItem, id: uuid, type: type });
|
||||
await layer?.api?.createItem!({ ...formItem, id: uuid});
|
||||
success = true;
|
||||
} catch (error) {
|
||||
toast.error(error.toString());
|
||||
@ -85,7 +84,7 @@ export const OverlayItemsIndexPage = ({url, type, parameterField, breadcrumbs, i
|
||||
if (success) {
|
||||
toast.success("New item created");
|
||||
}
|
||||
addItem({...formItem, user_created: user, type: type, id: uuid, layer: layer});
|
||||
addItem({...formItem, user_created: user, id: uuid, layer: layer});
|
||||
setLoading(false);
|
||||
setAddItemPopupType("");
|
||||
}
|
||||
@ -107,7 +106,6 @@ export const OverlayItemsIndexPage = ({url, type, parameterField, breadcrumbs, i
|
||||
}
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -123,7 +121,7 @@ export const OverlayItemsIndexPage = ({url, type, parameterField, breadcrumbs, i
|
||||
</div>
|
||||
<div className="tw-grid tw-grid-cols-1 md:tw-grid-cols-2 lg:tw-grid-cols-3 tw-gap-6 tw-pt-4">
|
||||
{
|
||||
items?.filter(i=>i.type === type).map((i, k) => {
|
||||
items?.filter(i=>i.layer?.name === layerName).map((i, k) => {
|
||||
return (
|
||||
<div key={k} className='tw-cursor-pointer tw-card tw-border-[1px] tw-border-base-300 tw-card-body tw-shadow-xl tw-bg-base-100 tw-text-base-content tw-p-4 tw-mb-4 tw-h-fit' onClick={() => navigate(url + getValue(i, parameterField))}>
|
||||
<HeaderView loading={loading} item={i} api={layer?.api} itemAvatarField={itemImageField} itemNameField={itemNameField} itemSubnameField={itemSubnameField} editCallback={() => navigate("/edit-item/" + i.id)} deleteCallback={() => deleteItem(i)}></HeaderView>
|
||||
@ -136,7 +134,7 @@ export const OverlayItemsIndexPage = ({url, type, parameterField, breadcrumbs, i
|
||||
|
||||
})
|
||||
}
|
||||
{addItemPopupType == "project" ?
|
||||
{addItemPopupType == "place" ?
|
||||
|
||||
<form ref={tabRef} autoComplete='off' onSubmit={e => submitNewItem(e)} >
|
||||
|
||||
@ -161,7 +159,7 @@ export const OverlayItemsIndexPage = ({url, type, parameterField, breadcrumbs, i
|
||||
</MapOverlayPage>
|
||||
|
||||
|
||||
{plusButton && <PlusButton triggerAction={() => { setAddItemPopupType("project"); scroll(); }} color={'#777'} collection='items' />}
|
||||
{plusButton && <PlusButton triggerAction={() => { setAddItemPopupType("place"); scroll(); }} color={'#777'} collection='items' />}
|
||||
|
||||
</>
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ export interface UtopiaMapProps {
|
||||
}
|
||||
|
||||
export interface LayerProps {
|
||||
id?: string,
|
||||
data?: Item[],
|
||||
children?: React.ReactNode,
|
||||
name: string,
|
||||
@ -21,7 +22,7 @@ export interface LayerProps {
|
||||
markerShape: string,
|
||||
markerDefaultColor: string,
|
||||
api?: ItemsApi<any>,
|
||||
itemType: string,
|
||||
itemType: ItemType,
|
||||
itemNameField?: string,
|
||||
itemSubnameField?: string,
|
||||
itemTextField?: string,
|
||||
@ -41,6 +42,12 @@ export interface LayerProps {
|
||||
clusterRef?: any
|
||||
}
|
||||
|
||||
export interface ItemType {
|
||||
name: string;
|
||||
[key: string]: any;
|
||||
|
||||
}
|
||||
|
||||
export class Item {
|
||||
id: string ;
|
||||
name: string;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user