mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
permissions and item index
This commit is contained in:
parent
ad13ecb682
commit
68ce808558
@ -7,6 +7,8 @@ import { AssetsProvider } from './hooks/useAssets'
|
|||||||
import { SetAssetsApi } from './SetAssetsApi'
|
import { SetAssetsApi } from './SetAssetsApi'
|
||||||
import { AssetsApi } from '../../types'
|
import { AssetsApi } from '../../types'
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||||
|
import { PermissionsProvider } from '../Map/hooks/usePermissions'
|
||||||
|
import { TagsProvider } from '../Map/hooks/useTags'
|
||||||
|
|
||||||
export function AppShell({ appName, nameWidth, children, assetsApi }: { appName: string, nameWidth?: number, children: React.ReactNode, assetsApi: AssetsApi }) {
|
export function AppShell({ appName, nameWidth, children, assetsApi }: { appName: string, nameWidth?: number, children: React.ReactNode, assetsApi: AssetsApi }) {
|
||||||
|
|
||||||
@ -15,28 +17,33 @@ export function AppShell({ appName, nameWidth, children, assetsApi }: { appName:
|
|||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<QueryClientProvider client={queryClient}>
|
<PermissionsProvider initialPermissions={[]}>
|
||||||
<BrowserRouter>
|
<TagsProvider initialTags={[]}>
|
||||||
<AssetsProvider>
|
<QueryClientProvider client={queryClient}>
|
||||||
<SetAssetsApi assetsApi={assetsApi}></SetAssetsApi>
|
<BrowserRouter>
|
||||||
<QuestsProvider initialOpen={true}>
|
<AssetsProvider>
|
||||||
<ToastContainer position="top-right"
|
<SetAssetsApi assetsApi={assetsApi}></SetAssetsApi>
|
||||||
autoClose={2000}
|
<QuestsProvider initialOpen={true}>
|
||||||
hideProgressBar
|
<ToastContainer position="top-right"
|
||||||
newestOnTop={false}
|
autoClose={2000}
|
||||||
closeOnClick
|
hideProgressBar
|
||||||
rtl={false}
|
newestOnTop={false}
|
||||||
pauseOnFocusLoss
|
closeOnClick
|
||||||
draggable
|
rtl={false}
|
||||||
pauseOnHover
|
pauseOnFocusLoss
|
||||||
theme="light" />
|
draggable
|
||||||
<NavBar appName={appName} nameWidth={nameWidth}></NavBar>
|
pauseOnHover
|
||||||
<div id="app-content" className="tw-flex tw-!pl-[77px]">
|
theme="light" />
|
||||||
{children}
|
<NavBar appName={appName} nameWidth={nameWidth}></NavBar>
|
||||||
</div>
|
<div id="app-content" className="tw-flex tw-!pl-[77px]">
|
||||||
</QuestsProvider>
|
{children}
|
||||||
</AssetsProvider>
|
</div>
|
||||||
</BrowserRouter>
|
</QuestsProvider>
|
||||||
</QueryClientProvider>
|
</AssetsProvider>
|
||||||
|
</BrowserRouter>
|
||||||
|
</QueryClientProvider>
|
||||||
|
</TagsProvider>
|
||||||
|
</PermissionsProvider>
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,6 +42,7 @@ export function HeaderView({ item, setItemFormPopup, hideMenu=false }: {
|
|||||||
|
|
||||||
|
|
||||||
const removeItemFromMap = async (event: React.MouseEvent<HTMLElement>) => {
|
const removeItemFromMap = async (event: React.MouseEvent<HTMLElement>) => {
|
||||||
|
event.stopPropagation();
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
let success = false;
|
let success = false;
|
||||||
try {
|
try {
|
||||||
@ -58,7 +59,10 @@ export function HeaderView({ item, setItemFormPopup, hideMenu=false }: {
|
|||||||
map.closePopup();
|
map.closePopup();
|
||||||
let params = new URLSearchParams(window.location.search);
|
let params = new URLSearchParams(window.location.search);
|
||||||
window.history.pushState({}, "", "/" + `${params? `?${params}` : ""}`);
|
window.history.pushState({}, "", "/" + `${params? `?${params}` : ""}`);
|
||||||
event.stopPropagation();
|
setModalOpen(false);
|
||||||
|
navigate("/");
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const openDeleteModal = async (event: React.MouseEvent<HTMLElement>) => {
|
const openDeleteModal = async (event: React.MouseEvent<HTMLElement>) => {
|
||||||
@ -90,10 +94,9 @@ export function HeaderView({ item, setItemFormPopup, hideMenu=false }: {
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='tw-col-span-1'>
|
<div className='tw-col-span-1' onClick={(e)=>e.stopPropagation()}>
|
||||||
{(item.layer?.api?.deleteItem || item.layer?.api?.updateItem)
|
{(item.layer?.api?.deleteItem || item.layer?.api?.updateItem)
|
||||||
&& ((user && owner?.id === user.id) || owner == undefined)
|
&& (hasUserPermission(item.layer.api?.collectionName!, "delete", item) || hasUserPermission(item.layer.api?.collectionName!, "update", item))
|
||||||
&& (hasUserPermission(item.layer.api?.collectionName!, "delete") || hasUserPermission(item.layer.api?.collectionName!, "update"))
|
|
||||||
&& !hideMenu &&
|
&& !hideMenu &&
|
||||||
<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">
|
||||||
@ -102,7 +105,7 @@ export function HeaderView({ item, setItemFormPopup, hideMenu=false }: {
|
|||||||
</svg>
|
</svg>
|
||||||
</label>
|
</label>
|
||||||
<ul tabIndex={0} className="tw-dropdown-content tw-menu tw-p-2 tw-shadow tw-bg-base-100 tw-rounded-box tw-z-1000">
|
<ul tabIndex={0} className="tw-dropdown-content tw-menu tw-p-2 tw-shadow tw-bg-base-100 tw-rounded-box tw-z-1000">
|
||||||
{((item.layer.api.updateItem && hasUserPermission(item.layer.api?.collectionName!, "update")) || item.layer.customEditLink) && <li>
|
{((item.layer.api.updateItem && hasUserPermission(item.layer.api?.collectionName!, "update", item)) || item.layer.customEditLink) && <li>
|
||||||
<a className="!tw-text-base-content tw-cursor-pointer" onClick={(e) => {
|
<a className="!tw-text-base-content tw-cursor-pointer" onClick={(e) => {
|
||||||
item.layer?.customEditLink && navigate(item.layer.customEditLink);
|
item.layer?.customEditLink && navigate(item.layer.customEditLink);
|
||||||
!item.layer?.customEditLink && openEditPopup(e);
|
!item.layer?.customEditLink && openEditPopup(e);
|
||||||
@ -113,7 +116,7 @@ export function HeaderView({ item, setItemFormPopup, hideMenu=false }: {
|
|||||||
</a>
|
</a>
|
||||||
</li>}
|
</li>}
|
||||||
|
|
||||||
{item.layer.api.deleteItem && hasUserPermission(item.layer.api?.collectionName!, "delete") && <li>
|
{item.layer.api.deleteItem && hasUserPermission(item.layer.api?.collectionName!, "delete", item) && <li>
|
||||||
<a className='tw-cursor-pointer !tw-text-error' onClick={openDeleteModal}>
|
<a className='tw-cursor-pointer !tw-text-error' onClick={openDeleteModal}>
|
||||||
{loading ? <span className="tw-loading tw-loading-spinner tw-loading-sm"></span>
|
{loading ? <span className="tw-loading tw-loading-spinner tw-loading-sm"></span>
|
||||||
:
|
:
|
||||||
|
|||||||
@ -9,11 +9,9 @@ import AddButton from "./Subcomponents/AddButton";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { ItemFormPopupProps } from "./Subcomponents/ItemFormPopup";
|
import { ItemFormPopupProps } from "./Subcomponents/ItemFormPopup";
|
||||||
import { ItemsProvider } from "./hooks/useItems";
|
import { ItemsProvider } from "./hooks/useItems";
|
||||||
import { TagsProvider } from "./hooks/useTags";
|
|
||||||
import { LayersProvider } from "./hooks/useLayers";
|
import { LayersProvider } from "./hooks/useLayers";
|
||||||
import { FilterProvider } from "./hooks/useFilter";
|
import { FilterProvider } from "./hooks/useFilter";
|
||||||
import { SearchControl } from "./Subcomponents/Controls/SearchControl";
|
import { SearchControl } from "./Subcomponents/Controls/SearchControl";
|
||||||
import { PermissionsProvider } from "./hooks/usePermissions";
|
|
||||||
import { LeafletRefsProvider } from "./hooks/useLeafletRefs";
|
import { LeafletRefsProvider } from "./hooks/useLeafletRefs";
|
||||||
import { LayerControl } from "./Subcomponents/Controls/LayerControl";
|
import { LayerControl } from "./Subcomponents/Controls/LayerControl";
|
||||||
import { QuestControl } from "./Subcomponents/Controls/QuestControl";
|
import { QuestControl } from "./Subcomponents/Controls/QuestControl";
|
||||||
@ -83,8 +81,6 @@ function UtopiaMap({
|
|||||||
<>
|
<>
|
||||||
|
|
||||||
<LayersProvider initialLayers={[]}>
|
<LayersProvider initialLayers={[]}>
|
||||||
<TagsProvider initialTags={[]}>
|
|
||||||
<PermissionsProvider initialPermissions={[]}>
|
|
||||||
<FilterProvider initialTags={[]}>
|
<FilterProvider initialTags={[]}>
|
||||||
<ItemsProvider initialItems={[]}>
|
<ItemsProvider initialItems={[]}>
|
||||||
<LeafletRefsProvider initialLeafletRefs={{}}>
|
<LeafletRefsProvider initialLeafletRefs={{}}>
|
||||||
@ -127,8 +123,6 @@ function UtopiaMap({
|
|||||||
</LeafletRefsProvider>
|
</LeafletRefsProvider>
|
||||||
</ItemsProvider>
|
</ItemsProvider>
|
||||||
</FilterProvider>
|
</FilterProvider>
|
||||||
</PermissionsProvider>
|
|
||||||
</TagsProvider>
|
|
||||||
</LayersProvider>
|
</LayersProvider>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { useCallback, useReducer, createContext, useContext } from "react";
|
import { useCallback, useReducer, createContext, useContext } from "react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { ItemsApi, LayerProps, Permission, PermissionAction } from "../../../types";
|
import { Item, ItemsApi, Permission, PermissionAction } from "../../../types";
|
||||||
import { useAuth } from "../../Auth";
|
import { useAuth } from "../../Auth";
|
||||||
|
|
||||||
type ActionType =
|
type ActionType =
|
||||||
@ -22,7 +22,7 @@ function usePermissionsManager(initialPermissions: Permission[]): {
|
|||||||
setPermissionApi: (api: ItemsApi<any>) => void;
|
setPermissionApi: (api: ItemsApi<any>) => void;
|
||||||
setPermissionData: (data: Permission[]) => void;
|
setPermissionData: (data: Permission[]) => void;
|
||||||
setAdminRole: (adminRole: string) => void;
|
setAdminRole: (adminRole: string) => void;
|
||||||
hasUserPermission: (collectionName: string, action: PermissionAction) => boolean;
|
hasUserPermission: (collectionName: string, action: PermissionAction, item?: Item) => boolean;
|
||||||
} {
|
} {
|
||||||
const [permissions, dispatch] = useReducer((state: Permission[], action: ActionType) => {
|
const [permissions, dispatch] = useReducer((state: Permission[], action: ActionType) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
@ -62,11 +62,30 @@ function usePermissionsManager(initialPermissions: Permission[]): {
|
|||||||
})
|
})
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const hasUserPermission = useCallback((collectionName: string, action: PermissionAction) => {
|
const hasUserPermission = useCallback(
|
||||||
if (permissions.length == 0) return true;
|
(collectionName: string, action: PermissionAction, item?: Item) => {
|
||||||
else if (user && user.role == adminRole) return true;
|
if (permissions.length === 0) return true;
|
||||||
else return permissions.some(p => p.action === action && p.collection === collectionName && p.role == user?.role)
|
else if (user && user.role === adminRole) return true;
|
||||||
}, [permissions, user]);
|
else {
|
||||||
|
return permissions.some(p =>
|
||||||
|
p.action === action &&
|
||||||
|
p.collection === collectionName &&
|
||||||
|
p.role === user?.role &&
|
||||||
|
(
|
||||||
|
// Wenn 'item' nicht gesetzt ist, ignorieren wir die Überprüfung von 'user_created'
|
||||||
|
!item || !p.permissions || !p.permissions._and ||
|
||||||
|
p.permissions._and.some(condition =>
|
||||||
|
condition.user_created &&
|
||||||
|
condition.user_created._eq === "$CURRENT_USER" &&
|
||||||
|
item.user_created.id === user?.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[permissions, user]
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { LatLng } from 'leaflet';
|
|||||||
import { PopupStartEndInput, StartEndView, TextView } from '../Map';
|
import { PopupStartEndInput, StartEndView, TextView } from '../Map';
|
||||||
import useWindowDimensions from '../Map/hooks/useWindowDimension';
|
import useWindowDimensions from '../Map/hooks/useWindowDimension';
|
||||||
import { useAddTag, useTags } from '../Map/hooks/useTags';
|
import { useAddTag, useTags } from '../Map/hooks/useTags';
|
||||||
import { useAddFilterTag, useResetFilterTags } from '../Map/hooks/useFilter';
|
import { useResetFilterTags } from '../Map/hooks/useFilter';
|
||||||
import { HeaderView } from '../Map/Subcomponents/ItemPopupComponents/HeaderView';
|
import { HeaderView } from '../Map/Subcomponents/ItemPopupComponents/HeaderView';
|
||||||
import { useHasUserPermission } from '../Map/hooks/usePermissions';
|
import { useHasUserPermission } from '../Map/hooks/usePermissions';
|
||||||
import {PlusButton} from './PlusButton';
|
import {PlusButton} from './PlusButton';
|
||||||
@ -19,7 +19,6 @@ import { hashTagRegex } from '../../Utils/HashTagRegex';
|
|||||||
import { randomColor } from '../../Utils/RandomColor';
|
import { randomColor } from '../../Utils/RandomColor';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { useAuth } from '../Auth';
|
import { useAuth } from '../Auth';
|
||||||
import { useLayers } from '../Map/hooks/useLayers';
|
|
||||||
|
|
||||||
export function OverlayItemProfile() {
|
export function OverlayItemProfile() {
|
||||||
|
|
||||||
@ -30,16 +29,13 @@ export function OverlayItemProfile() {
|
|||||||
const map = useMap();
|
const map = useMap();
|
||||||
const windowDimension = useWindowDimensions();
|
const windowDimension = useWindowDimensions();
|
||||||
|
|
||||||
const layers = useLayers();
|
const [addButton, setAddButton] = useState<boolean>(false);
|
||||||
|
|
||||||
|
|
||||||
const tags = useTags();
|
const tags = useTags();
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const [owner, setOwner] = useState<UserItem>();
|
|
||||||
const [offers, setOffers] = useState<Array<Tag>>([]);
|
|
||||||
const [needs, setNeeds] = useState<Array<Tag>>([]);
|
|
||||||
const [relations, setRelations] = useState<Array<Item>>([]);
|
const [relations, setRelations] = useState<Array<Item>>([]);
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState<number>(1);
|
const [activeTab, setActiveTab] = useState<number>(1);
|
||||||
@ -66,11 +62,13 @@ export function OverlayItemProfile() {
|
|||||||
scroll();
|
scroll();
|
||||||
}, [addItemPopupType])
|
}, [addItemPopupType])
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
const itemId = location.pathname.split("/")[2];
|
const itemId = location.pathname.split("/")[2];
|
||||||
const item = items.find(i => i.id === itemId);
|
const item = items.find(i => i.id === itemId);
|
||||||
item && setItem(item);
|
item && setItem(item);
|
||||||
|
hasUserPermission("items", "update", item) && setAddButton(true);
|
||||||
const bounds = map.getBounds();
|
const bounds = map.getBounds();
|
||||||
const x = bounds.getEast() - bounds.getWest()
|
const x = bounds.getEast() - bounds.getWest()
|
||||||
if (windowDimension.width > 768)
|
if (windowDimension.width > 768)
|
||||||
@ -82,25 +80,15 @@ export function OverlayItemProfile() {
|
|||||||
setActiveTab(1);
|
setActiveTab(1);
|
||||||
}, [location])
|
}, [location])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setOffers([]);
|
|
||||||
setNeeds([]);
|
|
||||||
setRelations([]);
|
setRelations([]);
|
||||||
setOwner(undefined);
|
|
||||||
item?.layer?.itemOwnerField && setOwner(getValue(item, item.layer?.itemOwnerField));
|
|
||||||
item.layer?.itemOffersField && getValue(item, item.layer.itemOffersField).map(o => {
|
|
||||||
const tag = tags.find(t => t.id === o.tags_id);
|
|
||||||
tag && setOffers(current => [...current, tag])
|
|
||||||
})
|
|
||||||
item.layer?.itemNeedsField && getValue(item, item.layer.itemNeedsField).map(n => {
|
|
||||||
const tag = tags.find(t => t.id === n.tags_id);
|
|
||||||
tag && setNeeds(current => [...current, tag])
|
|
||||||
})
|
|
||||||
item.relations?.map(r => {
|
item.relations?.map(r => {
|
||||||
const item = items.find(i => i.id == r.related_items_id)
|
const item = items.find(i => i.id == r.related_items_id)
|
||||||
item && setRelations(current => [...current, item])
|
item && setRelations(current => [...current, item])
|
||||||
})
|
})
|
||||||
}, [item])
|
|
||||||
|
}, [item, items])
|
||||||
|
|
||||||
const submitNewItem = async (evt: any, type: string) => {
|
const submitNewItem = async (evt: any, type: string) => {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
@ -135,7 +123,7 @@ export function OverlayItemProfile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const linkItem = async (id: string) => {
|
const linkItem = async (id: string) => {
|
||||||
let new_relations = item.relations;
|
let new_relations = item.relations|| [] ;
|
||||||
new_relations?.push({ items_id: item.id, related_items_id: id })
|
new_relations?.push({ items_id: item.id, related_items_id: id })
|
||||||
|
|
||||||
const updatedItem = { id: item.id, relations: new_relations }
|
const updatedItem = { id: item.id, relations: new_relations }
|
||||||
@ -146,14 +134,14 @@ export function OverlayItemProfile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MapOverlayPage className='tw-mx-4 tw-mt-4 tw-max-h-[calc(100dvh-96px)] tw-h-[calc(100dvh-96px)] md:tw-w-[calc(50%-32px)] tw-w-[calc(100%-32px)] tw-max-w-3xl !tw-left-auto tw-top-0 tw-bottom-0'>
|
<MapOverlayPage className='tw-mx-4 tw-mt-4 tw-max-h-[calc(100dvh-96px)] tw-h-[calc(100dvh-96px)] md:tw-w-[calc(50%-32px)] tw-w-[calc(100%-32px)] tw-min-w-80 tw-max-w-3xl !tw-left-auto tw-top-0 tw-bottom-0'>
|
||||||
{item &&
|
{item &&
|
||||||
<>
|
<>
|
||||||
<div className='tw-flex tw-flex-row'>
|
<div className='tw-flex tw-flex-row'>
|
||||||
<div className="tw-grow">
|
<div className="tw-grow">
|
||||||
<p className="tw-text-3xl tw-font-semibold">{item.layer?.itemAvatarField && getValue(item, item.layer.itemAvatarField) && <img className='tw-w-20 tw-h-20 tw-rounded-full tw-inline' src={`https://api.utopia-lab.org/assets/${getValue(item, item.layer.itemAvatarField)}?width=160&heigth=160`}></img>} {item.layer?.itemNameField && getValue(item, item.layer.itemNameField)}</p>
|
<p className="tw-text-3xl tw-font-semibold">{item.layer?.itemAvatarField && getValue(item, item.layer.itemAvatarField) && <img className='tw-w-20 tw-h-20 tw-rounded-full tw-inline' src={`https://api.utopia-lab.org/assets/${getValue(item, item.layer.itemAvatarField)}?width=160&heigth=160`}></img>} {item.layer?.itemNameField && getValue(item, item.layer.itemNameField)}</p>
|
||||||
</div>
|
</div>
|
||||||
{(item.layer?.api?.updateItem && hasUserPermission(item.layer.api?.collectionName!, "update")) ?
|
{(item.layer?.api?.updateItem && hasUserPermission(item.layer.api?.collectionName!, "update", item)) ?
|
||||||
<a className='tw-self-center tw-btn tw-btn-sm tw-mr-4 tw-cursor-pointer' onClick={() => navigate("/edit-item/" + item.id)}>
|
<a className='tw-self-center tw-btn tw-btn-sm tw-mr-4 tw-cursor-pointer' onClick={() => navigate("/edit-item/" + item.id)}>
|
||||||
<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">
|
||||||
<path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
|
<path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
|
||||||
@ -206,7 +194,7 @@ export function OverlayItemProfile() {
|
|||||||
</div>
|
</div>
|
||||||
</form> : <></>
|
</form> : <></>
|
||||||
}
|
}
|
||||||
<PlusButton triggerAction={() => { setAddItemPopupType("project"); scroll() }} color={item.color}></PlusButton>
|
{ addButton && <PlusButton triggerAction={() => { setAddItemPopupType("project"); scroll() }} color={item.color}></PlusButton>}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,11 +1,8 @@
|
|||||||
import { useHasUserPermission } from "../Map/hooks/usePermissions";
|
import { useHasUserPermission, usePermissions } from "../Map/hooks/usePermissions";
|
||||||
|
import { useAuth } from "../Auth";
|
||||||
|
|
||||||
export function PlusButton({ triggerAction, color, collection="items" }: { triggerAction: any, color: string, collection?:string }) {
|
export function PlusButton({ triggerAction, color, collection="items" }: { triggerAction: any, color: string, collection?:string }) {
|
||||||
|
const hasUserPermission = useHasUserPermission();
|
||||||
const hasUserPermission = useHasUserPermission();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>{hasUserPermission(collection, "create") &&
|
<>{hasUserPermission(collection, "create") &&
|
||||||
<div className="tw-dropdown tw-dropdown-top tw-dropdown-end tw-dropdown-hover tw-z-500 tw-absolute tw-right-4 tw-bottom-4" >
|
<div className="tw-dropdown tw-dropdown-top tw-dropdown-end tw-dropdown-hover tw-z-500 tw-absolute tw-right-4 tw-bottom-4" >
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { Item, ItemsApi } from '../../types';
|
|||||||
import { getValue } from '../../Utils/GetValue';
|
import { getValue } from '../../Utils/GetValue';
|
||||||
import { TextView } from '../Map';
|
import { TextView } from '../Map';
|
||||||
import { useAssetApi } from '../AppShell/hooks/useAssets';
|
import { useAssetApi } from '../AppShell/hooks/useAssets';
|
||||||
import { PlusButton } from '../Profile';
|
import { PlusButton } from '../Profile/PlusButton';
|
||||||
import { TextInput, TextAreaInput } from '../Input';
|
import { TextInput, TextAreaInput } from '../Input';
|
||||||
import { useAddTag, useTags } from '../Map/hooks/useTags';
|
import { useAddTag, useTags } from '../Map/hooks/useTags';
|
||||||
import { useAddItem } from '../Map/hooks/useItems';
|
import { useAddItem } from '../Map/hooks/useItems';
|
||||||
@ -14,6 +14,7 @@ import { hashTagRegex } from '../../Utils/HashTagRegex';
|
|||||||
import { randomColor } from '../../Utils/RandomColor';
|
import { randomColor } from '../../Utils/RandomColor';
|
||||||
import { useAuth } from '../Auth';
|
import { useAuth } from '../Auth';
|
||||||
import { useLayers } from '../Map/hooks/useLayers';
|
import { useLayers } from '../Map/hooks/useLayers';
|
||||||
|
import { PermissionsProvider } from '../Map/hooks/usePermissions';
|
||||||
|
|
||||||
|
|
||||||
type breadcrumb = {
|
type breadcrumb = {
|
||||||
@ -22,7 +23,7 @@ type breadcrumb = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const ItemsIndexPage = ({ api, url, parameterField, breadcrumbs, itemNameField, itemTextField, itemImageField, itemSymbolField }: { api: ItemsApi<any>, url: string, parameterField: string, breadcrumbs: Array<breadcrumb>, itemNameField: string, itemTextField: string, itemImageField: string, itemSymbolField: string }) => {
|
export const ItemsIndexPage = ({ api, url, parameterField, breadcrumbs, itemNameField, itemTextField, itemImageField, itemSymbolField, children }: { api: ItemsApi<any>, url: string, parameterField: string, breadcrumbs: Array<breadcrumb>, itemNameField: string, itemTextField: string, itemImageField: string, itemSymbolField: string, children?: ReactNode }) => {
|
||||||
|
|
||||||
console.log(itemSymbolField);
|
console.log(itemSymbolField);
|
||||||
|
|
||||||
@ -62,8 +63,6 @@ export const ItemsIndexPage = ({ api, url, parameterField, breadcrumbs, itemName
|
|||||||
|
|
||||||
const layers = useLayers();
|
const layers = useLayers();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const submitNewItem = async (evt: any, type: string) => {
|
const submitNewItem = async (evt: any, type: string) => {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
const formItem: Item = {} as Item;
|
const formItem: Item = {} as Item;
|
||||||
@ -98,6 +97,7 @@ export const ItemsIndexPage = ({ api, url, parameterField, breadcrumbs, itemName
|
|||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<main className="tw-flex-1 tw-overflow-y-auto tw-pt-2 tw-px-6 tw-bg-base-200 tw-min-w-80 tw-flex tw-justify-center" >
|
<main className="tw-flex-1 tw-overflow-y-auto tw-pt-2 tw-px-6 tw-bg-base-200 tw-min-w-80 tw-flex tw-justify-center" >
|
||||||
<div className=' tw-w-full xl:tw-max-w-6xl'>
|
<div className=' tw-w-full xl:tw-max-w-6xl'>
|
||||||
{breadcrumbs &&
|
{breadcrumbs &&
|
||||||
@ -121,10 +121,9 @@ export const ItemsIndexPage = ({ api, url, parameterField, breadcrumbs, itemName
|
|||||||
{
|
{
|
||||||
items?.map((i, k) => {
|
items?.map((i, k) => {
|
||||||
return (
|
return (
|
||||||
<Link key={k} to={url + getValue(i, parameterField)}>
|
|
||||||
|
|
||||||
|
|
||||||
<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-p-4 tw-mb-4 tw-h-fit' onClick={() => navigate('/item/' + i.id)}>
|
<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))}>
|
||||||
|
|
||||||
<div className='tw-grid tw-grid-cols-6 tw-pb-2'>
|
<div className='tw-grid tw-grid-cols-6 tw-pb-2'>
|
||||||
<div className='tw-col-span-5'>
|
<div className='tw-col-span-5'>
|
||||||
@ -149,7 +148,6 @@ export const ItemsIndexPage = ({ api, url, parameterField, breadcrumbs, itemName
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</Link>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
})
|
})
|
||||||
@ -173,7 +171,8 @@ export const ItemsIndexPage = ({ api, url, parameterField, breadcrumbs, itemName
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<PlusButton triggerAction={() => {setAddItemPopupType("project"); scroll();}} color={'#777'} />
|
<PlusButton triggerAction={() => {setAddItemPopupType("project"); scroll();}} color={'#777'} collection='items'/>
|
||||||
|
{children}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
14
src/types.ts
14
src/types.ts
@ -122,12 +122,22 @@ export type Profile = {
|
|||||||
geoposition?: Geometry
|
geoposition?: Geometry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type PermissionCondition = {
|
||||||
|
user_created?: {
|
||||||
|
_eq: string; // Erwartet den speziellen Wert "$CURRENT_USER" oder eine spezifische UUID
|
||||||
|
};
|
||||||
|
// Hier können weitere Bedingungen nach Bedarf hinzugefügt werden
|
||||||
|
};
|
||||||
|
|
||||||
export type Permission = {
|
export type Permission = {
|
||||||
id?: string;
|
id?: string;
|
||||||
role: string;
|
role: string;
|
||||||
collection: string;
|
collection: string;
|
||||||
action: PermissionAction
|
action: PermissionAction;
|
||||||
}
|
permissions?: { // Optional, für spezifische Bedingungen wie `user_created`
|
||||||
|
_and: PermissionCondition[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type PermissionAction = "create"|"read"|"update"|"delete";
|
export type PermissionAction = "create"|"read"|"update"|"delete";
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user