owned item basics and item field adjustments

This commit is contained in:
Anton 2024-01-06 17:00:44 +01:00
parent 49ab4a9989
commit a8112ee604
6 changed files with 72 additions and 42 deletions

View File

@ -14,9 +14,26 @@ import { useLocation } from 'react-router-dom';
import { useAssetApi } from '../AppShell/hooks/useAssets' import { useAssetApi } from '../AppShell/hooks/useAssets'
import { getValue } from '../../Utils/GetValue' import { getValue } from '../../Utils/GetValue'
export const Layer = (props: LayerProps) => { export const Layer = ( {
data,
const [itemFormPopup, setItemFormPopup] = useState<ItemFormPopupProps | null>(null); children,
name='places',
menuIcon='MapPinIcon',
menuText='add new place',
menuColor='#2E7D32',
markerIcon='circle-solid',
markerShape='circle',
markerDefaultColor='#777',
api,
itemTitleField='name',
itemTextField='text',
itemAvatarField,
itemColorField,
itemOwnerField,
setItemFormPopup,
itemFormPopup,
clusterRef
}: LayerProps) => {
const filterTags = useFilterTags(); const filterTags = useFilterTags();
@ -41,15 +58,15 @@ export const Layer = (props: LayerProps) => {
useEffect(() => { useEffect(() => {
props.data && setItemsData(props); data && setItemsData({data, children, name, menuIcon, menuText, menuColor, markerIcon, markerShape, markerDefaultColor, api, itemTitleField, itemTextField, itemAvatarField, itemColorField, setItemFormPopup, itemFormPopup, clusterRef});
props.api && setItemsApi(props); api && setItemsApi({data, children, name, menuIcon, menuText, menuColor, markerIcon, markerShape, markerDefaultColor, api, itemTitleField, itemTextField, itemAvatarField, itemColorField, setItemFormPopup, itemFormPopup, clusterRef});
}, [props.data, props.api]) }, [data, api])
useMapEvents({ useMapEvents({
popupopen: (e) => { popupopen: (e) => {
const item = Object.entries(leafletRefs).find(r => r[1].popup == e.popup)?.[1].item; const item = Object.entries(leafletRefs).find(r => r[1].popup == e.popup)?.[1].item;
if (item?.layer?.name == props.name && window.location.pathname.split("/")[2] != item.id) { if (item?.layer?.name == name && window.location.pathname.split("/")[2] != item.id) {
window.history.pushState({}, "", `/${props.name}/${item.id}`) window.history.pushState({}, "", `/${name}/${item.id}`)
let title = ""; let title = "";
if(item.name) title = item.name; if(item.name) title = item.name;
else if (item.layer?.itemTitleField) title = getValue(item, item.layer.itemTitleField); else if (item.layer?.itemTitleField) title = getValue(item, item.layer.itemTitleField);
@ -63,13 +80,13 @@ export const Layer = (props: LayerProps) => {
map.closePopup(); map.closePopup();
} }
else { else {
if (window.location.pathname.split("/")[1] == props.name) { if (window.location.pathname.split("/")[1] == name) {
if (window.location.pathname.split("/")[2]) { if (window.location.pathname.split("/")[2]) {
const id = window.location.pathname.split("/")[2] const id = window.location.pathname.split("/")[2]
const marker = leafletRefs[id]?.marker; const marker = leafletRefs[id]?.marker;
if (marker && marker != null) { if (marker && marker != null) {
marker !== null && props.clusterRef?.current?.zoomToShowLayer(marker, () => { marker !== null && clusterRef?.current?.zoomToShowLayer(marker, () => {
marker.openPopup(); marker.openPopup();
}); });
const item = leafletRefs[id]?.item; const item = leafletRefs[id]?.item;
@ -96,7 +113,7 @@ export const Layer = (props: LayerProps) => {
{items && {items &&
items. items.
filter(item => item.text). filter(item => item.text).
filter(item => item.layer?.name === props.name)?. filter(item => item.layer?.name === name)?.
filter(item => filter(item =>
filterTags.length == 0 ? item : filterTags.every(tag => getItemTags(item).some(filterTag => filterTag.id === tag.id)))?. filterTags.length == 0 ? item : filterTags.every(tag => getItemTags(item).some(filterTag => filterTag.id === tag.id)))?.
filter(item => { filter(item => {
@ -108,13 +125,13 @@ export const Layer = (props: LayerProps) => {
map((item: Item) => { map((item: Item) => {
const tags = getItemTags(item); const tags = getItemTags(item);
let color1 = "#666"; let color1 = markerDefaultColor;
let color2 = "RGBA(35, 31, 32, 0.2)"; let color2 = "RGBA(35, 31, 32, 0.2)";
if (props.itemColorField) color1 = getValue(item, props.itemColorField); if (itemColorField) color1 = getValue(item, itemColorField);
else if (tags && tags[0]) { else if (tags && tags[0]) {
color1 = tags[0].color; color1 = tags[0].color;
} }
if (tags && tags[0] && props.itemColorField) color2 = tags[0].color; if (tags && tags[0] && itemColorField) color2 = tags[0].color;
else if (tags && tags[1]) { else if (tags && tags[1]) {
color2 = tags[1].color; color2 = tags[1].color;
} }
@ -122,19 +139,20 @@ export const Layer = (props: LayerProps) => {
<Marker ref={(r) => { <Marker ref={(r) => {
if (!(item.id in leafletRefs)) if (!(item.id in leafletRefs))
r && addMarker(item, r); r && addMarker(item, r);
}} icon={MarkerIconFactory(props.markerShape, color1, color2, props.markerIcon)} key={item.id} position={[item.position.coordinates[1], item.position.coordinates[0]]}> }} icon={MarkerIconFactory(markerShape, color1, color2, markerIcon)} key={item.id} position={[item.position.coordinates[1], item.position.coordinates[0]]}>
{ {
(props.children && React.Children.toArray(props.children).some(child => React.isValidElement(child) && child.props.__TYPE === "ItemView") ? (children && React.Children.toArray(children).some(child => React.isValidElement(child) && child.props.__TYPE === "ItemView") ?
React.Children.toArray(props.children).map((child) => React.Children.toArray(children).map((child) =>
React.isValidElement(child) && child.props.__TYPE === "ItemView" ? React.isValidElement(child) && child.props.__TYPE === "ItemView" ?
<ItemViewPopup ref={(r) => { <ItemViewPopup ref={(r) => {
if (!(item.id in leafletRefs)) if (!(item.id in leafletRefs))
r && addPopup(item, r as Popup); r && addPopup(item, r as Popup);
}} key={item.id + item.name} }} key={item.id + item.name}
title={props.itemTitleField && item ? getValue(item, props.itemTitleField) : undefined} title={itemTitleField && item ? getValue(item, itemTitleField) : undefined}
avatar={props.itemAvatarField && item ? assetsApi.url + getValue(item, props.itemAvatarField) : undefined} avatar={itemAvatarField && item ? assetsApi.url + getValue(item, itemAvatarField) : undefined}
owner={itemOwnerField && item ? getValue(item, itemOwnerField) : undefined}
item={item} item={item}
setItemFormPopup={props.setItemFormPopup}> setItemFormPopup={setItemFormPopup}>
{child} {child}
</ItemViewPopup> </ItemViewPopup>
: "" : ""
@ -144,29 +162,30 @@ export const Layer = (props: LayerProps) => {
<ItemViewPopup key={item.id + item.name} ref={(r) => { <ItemViewPopup key={item.id + item.name} ref={(r) => {
if (!(item.id in leafletRefs)) if (!(item.id in leafletRefs))
r && addPopup(item, r as Popup); r && addPopup(item, r as Popup);
}} title={props.itemTitleField && item ? getValue(item, props.itemTitleField) : undefined} }} title={itemTitleField && item ? getValue(item, itemTitleField) : undefined}
avatar={props.itemAvatarField && item ? assetsApi.url + getValue(item, props.itemAvatarField) : undefined} avatar={itemAvatarField && item ? assetsApi.url + getValue(item, itemAvatarField) : undefined}
owner={itemOwnerField && item ? getValue(item, itemOwnerField) : undefined}
item={item} item={item}
setItemFormPopup={props.setItemFormPopup} /> setItemFormPopup={setItemFormPopup} />
</>) </>)
} }
<Tooltip offset={[0, -38]} direction='top'>{item.name? item.name : getValue(item, props.itemTitleField)}</Tooltip> <Tooltip offset={[0, -38]} direction='top'>{item.name? item.name : getValue(item, itemTitleField)}</Tooltip>
</Marker> </Marker>
); );
}) })
} }
{//{props.children}} {//{children}}
} }
{props.itemFormPopup && props.itemFormPopup.layer!.name == props.name && {itemFormPopup && itemFormPopup.layer!.name == name &&
(props.children && React.Children.toArray(props.children).some(child => React.isValidElement(child) && child.props.__TYPE === "ItemForm") ? (children && React.Children.toArray(children).some(child => React.isValidElement(child) && child.props.__TYPE === "ItemForm") ?
React.Children.toArray(props.children).map((child) => React.Children.toArray(children).map((child) =>
React.isValidElement(child) && child.props.__TYPE === "ItemForm" ? React.isValidElement(child) && child.props.__TYPE === "ItemForm" ?
<ItemFormPopup key={props.setItemFormPopup?.name} position={props.itemFormPopup!.position} layer={props.itemFormPopup!.layer} setItemFormPopup={setItemFormPopup} item={props.itemFormPopup!.item} >{child}</ItemFormPopup> <ItemFormPopup key={setItemFormPopup?.name} position={itemFormPopup!.position} layer={itemFormPopup!.layer} setItemFormPopup={setItemFormPopup} item={itemFormPopup!.item} >{child}</ItemFormPopup>
: "" : ""
) )
: :
<> <>
<ItemFormPopup position={props.itemFormPopup!.position} layer={props.itemFormPopup!.layer} setItemFormPopup={setItemFormPopup} item={props.itemFormPopup!.item} /> <ItemFormPopup position={itemFormPopup!.position} layer={itemFormPopup!.layer} setItemFormPopup={setItemFormPopup} item={itemFormPopup!.item} />
</>) </>)
} }
</> </>

View File

@ -18,7 +18,7 @@ export interface ItemFormPopupProps {
layer: LayerProps, layer: LayerProps,
item?: Item, item?: Item,
children?: React.ReactNode, children?: React.ReactNode,
setItemFormPopup: React.Dispatch<React.SetStateAction<any>> setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>
} }
export function ItemFormPopup(props: ItemFormPopupProps) { export function ItemFormPopup(props: ItemFormPopupProps) {
@ -76,23 +76,23 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
map.closePopup(); map.closePopup();
} }
else { else {
const uuid = crypto.randomUUID();
let success = false; let success = false;
try { try {
await props.layer.api?.createItem!({...formItem, id: crypto.randomUUID() }); await props.layer.api?.createItem!({...formItem, id: uuid });
success = true; success = true;
} catch (error) { } catch (error) {
toast.error(error.toString()); toast.error(error.toString());
} }
if(success) { if(success) {
addItem({...formItem, id: crypto.randomUUID(), layer: props.layer, user_created: user}); addItem({...formItem, id: uuid, layer: props.layer, user_created: user});
toast.success("New item created"); toast.success("New item created");
resetFilterTags(); resetFilterTags();
} }
setSpinner(false); setSpinner(false);
map.closePopup(); map.closePopup();
} }
props.setItemFormPopup(null); props.setItemFormPopup!(null);
} }

View File

@ -7,13 +7,16 @@ import { Item } from "../../../../types";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import { useHasUserPermission } from "../../hooks/usePermissions"; import { useHasUserPermission } from "../../hooks/usePermissions";
import { timeAgo } from "../../../../Utils/TimeAgo"; import { timeAgo } from "../../../../Utils/TimeAgo";
import { useAuth } from "../../../Auth";
import { useEffect } from "react";
export function HeaderView({ item, title, avatar, setItemFormPopup }: { export function HeaderView({ item, title, avatar, owner, setItemFormPopup }: {
item: Item, item: Item,
title?: string, title?: string,
avatar?: string, avatar?: string,
owner?: string,
setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>> setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>
}) { }) {
@ -23,6 +26,9 @@ export function HeaderView({ item, title, avatar, setItemFormPopup }: {
const map = useMap(); const map = useMap();
const hasUserPermission = useHasUserPermission(); const hasUserPermission = useHasUserPermission();
const { user } = useAuth();
const removeItemFromMap = async (event: React.MouseEvent<HTMLElement>) => { const removeItemFromMap = async (event: React.MouseEvent<HTMLElement>) => {
setLoading(true); setLoading(true);
let success = false; let success = false;
@ -66,7 +72,9 @@ export function HeaderView({ item, title, avatar, setItemFormPopup }: {
</div> </div>
</div> </div>
<div className='tw-col-span-1'> <div className='tw-col-span-1'>
{(item.layer?.api?.deleteItem || item.layer?.api?.updateItem) && (hasUserPermission(item.layer.api?.collectionName!, "delete") || hasUserPermission(item.layer.api?.collectionName!, "update")) && {(item.layer?.api?.deleteItem || item.layer?.api?.updateItem)
&& ((user && owner === user.id) || owner == undefined)
&& (hasUserPermission(item.layer.api?.collectionName!, "delete") || hasUserPermission(item.layer.api?.collectionName!, "update")) &&
<div className="tw-dropdown tw-dropdown-bottom"> <div className="tw-dropdown tw-dropdown-bottom">
<label tabIndex={0} className="tw-bg-base-100 tw-btn tw-m-1 tw-leading-3 tw-border-none tw-min-h-0 tw-h-6"> <label tabIndex={0} className="tw-bg-base-100 tw-btn tw-m-1 tw-leading-3 tw-border-none tw-min-h-0 tw-h-6">
<svg xmlns="http://www.w3.org/2000/svg" className="tw-h-5 tw-w-5" viewBox="0 0 20 20" fill="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" className="tw-h-5 tw-w-5" viewBox="0 0 20 20" fill="currentColor">

View File

@ -3,9 +3,9 @@ import { Link } from 'react-router-dom'
import { getValue } from '../../../../Utils/GetValue' import { getValue } from '../../../../Utils/GetValue'
import { Item } from '../../../../types' import { Item } from '../../../../types'
export const PopupButton = ({url, parameter, text, color, item} : {url: string, parameter: string, text: string, color : string, item? : Item}) => { export const PopupButton = ({url, parameterField, text, color = 'oklch(var(--p))', colorField, item} : {url: string, parameterField?: string, text: string, color? : string, colorField?: string, item? : Item}) => {
return ( return (
<Link to={`${url}/${getValue(item,parameter)}`}><button style={{backgroundColor: getValue(item,color)}} className="tw-btn tw-text-white tw-btn-sm tw-float-right -tw-mt-2">{text}</button></Link> <Link to={`${url}/${parameterField? getValue(item,parameterField):``}`}><button style={{backgroundColor: `${colorField? getValue(item,colorField) : color}`}} className="tw-btn tw-text-white tw-btn-sm tw-float-right -tw-mt-2">{text}</button></Link>
) )
} }

View File

@ -14,6 +14,7 @@ export interface ItemViewPopupProps {
children?: React.ReactNode; children?: React.ReactNode;
title?: string; title?: string;
avatar?: string; avatar?: string;
owner?: string,
setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>> setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>
} }
@ -25,7 +26,7 @@ export const ItemViewPopup = React.forwardRef((props: ItemViewPopupProps, ref: a
return ( return (
<LeafletPopup ref={ref} maxHeight={377} minWidth={275} maxWidth={275} autoPanPadding={[20, 80]}> <LeafletPopup ref={ref} maxHeight={377} minWidth={275} maxWidth={275} autoPanPadding={[20, 80]}>
<div className='tw-bg-base-100 tw-text-base-content'> <div className='tw-bg-base-100 tw-text-base-content'>
<HeaderView item={props.item} title={props.title} avatar={props.avatar} setItemFormPopup={props.setItemFormPopup} /> <HeaderView item={props.item} title={props.title} avatar={props.avatar} owner={props.owner} setItemFormPopup={props.setItemFormPopup} />
<div className='tw-overflow-y-auto tw-overflow-x-hidden tw-max-h-64'> <div className='tw-overflow-y-auto tw-overflow-x-hidden tw-max-h-64'>
{props.children ? {props.children ?

View File

@ -22,8 +22,10 @@ export interface LayerProps {
markerDefaultColor: string, markerDefaultColor: string,
api?: ItemsApi<any>, api?: ItemsApi<any>,
itemTitleField?: string, itemTitleField?: string,
itemTextField?: string,
itemAvatarField?: string, itemAvatarField?: string,
itemColorField?: string, itemColorField?: string,
itemOwnerField?: string,
setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>, setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>,
itemFormPopup?: ItemFormPopupProps | null, itemFormPopup?: ItemFormPopupProps | null,
clusterRef?: React.MutableRefObject<any> clusterRef?: React.MutableRefObject<any>