some adjustments for dynamic maps

This commit is contained in:
Anton Tranelis 2024-04-22 22:38:39 +02:00
parent e78784b337
commit 5bbd4714b6
10 changed files with 59 additions and 52 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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])

View File

@ -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);
}
}
}

View File

@ -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>

View File

@ -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");

View File

@ -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)

View File

@ -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} />

View File

@ -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' />}
</>

View File

@ -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;