diff --git a/.github/workflows/test.backend.seed.yml b/.github/workflows/test.backend.seed.yml index 7bd745ae..225eeacd 100644 --- a/.github/workflows/test.backend.seed.yml +++ b/.github/workflows/test.backend.seed.yml @@ -40,7 +40,7 @@ jobs: sudo chmod 777 -R ./data docker compose -f docker-compose.yml up -d sleep 5 - cd backend && ./seed.sh + cd backend && ./push.sh && ./seed.sh working-directory: ${{env.WORKING_DIRECTORY}} #build-development: diff --git a/app/src/pages/MapContainer.tsx b/app/src/pages/MapContainer.tsx index b80c9907..f04207fe 100644 --- a/app/src/pages/MapContainer.tsx +++ b/app/src/pages/MapContainer.tsx @@ -113,6 +113,7 @@ function MapContainer({ layers, map }: { layers: LayerProps[]; map: any }) { public_edit_items={layer.public_edit_items} listed={layer.listed} api={apis.find((api) => api.id === layer.id)?.api} + item_default_name={layer.item_default_name} > {layer.itemType.show_start_end && } diff --git a/backend/README.md b/backend/README.md index b1af27d0..58276126 100644 --- a/backend/README.md +++ b/backend/README.md @@ -6,6 +6,7 @@ To run the backend you can simply execute To fill in all required data execute the following commands in order: ``` cd backend +./push.sh ./seed.sh ``` @@ -22,6 +23,8 @@ npx directus-sync pull \ --directus-password admin123 ``` +You can run `./pull.sh` to run this command and modify it via `export PROJECT=...` for a different project configuration. + ## Push Data from Harddrive to Docker To push local changes or to seed directus use the following command @@ -33,6 +36,8 @@ npx directus-sync push \ --directus-password admin123 ``` +You can run `./push.sh` to run this command and modify it via `export PROJECT=...` for a different project configuration. + ## Seed Data for local development In order to seed the development data, run the script `backend/seed.sh`. diff --git a/backend/directus-config/development/seed/Themes.json b/backend/directus-config/development/seed/Themes.json new file mode 100644 index 00000000..993dfffc --- /dev/null +++ b/backend/directus-config/development/seed/Themes.json @@ -0,0 +1,49 @@ +{ + "collection": "Themes", + "meta": { + "insert_order": 1, + "create": true, + "update": true, + "delete": true, + "preserve_ids": true, + "ignore_on_update": [] + }, + "data": [ + { + "_sync_id": "theme-light", + "theme": "light" + }, + { + "_sync_id": "theme-dark", + "theme": "dark" + }, + { + "_sync_id": "theme-valentine", + "theme": "valentine" + }, + { + "_sync_id": "theme-retro", + "theme": "retro" + }, + { + "_sync_id": "theme-aqua", + "theme": "aqua" + }, + { + "_sync_id": "theme-cyberpunk", + "theme": "cyberpunk" + }, + { + "_sync_id": "theme-caramellatte", + "theme": "caramellatte" + }, + { + "_sync_id": "theme-abyss", + "theme": "abyss" + }, + { + "_sync_id": "theme-silk", + "theme": "silk" + } + ] +} \ No newline at end of file diff --git a/backend/directus-config/development/snapshot/fields/layers/item_default_name.json b/backend/directus-config/development/snapshot/fields/layers/item_default_name.json new file mode 100644 index 00000000..011e4bfb --- /dev/null +++ b/backend/directus-config/development/snapshot/fields/layers/item_default_name.json @@ -0,0 +1,43 @@ +{ + "collection": "layers", + "field": "item_default_name", + "type": "string", + "meta": { + "collection": "layers", + "conditions": null, + "display": null, + "display_options": null, + "field": "item_default_name", + "group": null, + "hidden": false, + "interface": "input", + "note": null, + "options": null, + "readonly": false, + "required": false, + "sort": 16, + "special": null, + "translations": null, + "validation": null, + "validation_message": null, + "width": "half" + }, + "schema": { + "name": "item_default_name", + "table": "layers", + "data_type": "character varying", + "default_value": "item", + "max_length": 255, + "numeric_precision": null, + "numeric_scale": null, + "is_nullable": true, + "is_unique": false, + "is_indexed": false, + "is_primary_key": false, + "is_generated": false, + "generation_expression": null, + "has_auto_increment": false, + "foreign_key_table": null, + "foreign_key_column": null + } +} diff --git a/backend/directus-config/development/snapshot/fields/layers/item_presets.json b/backend/directus-config/development/snapshot/fields/layers/item_presets.json index b365ccb1..8d1c2617 100644 --- a/backend/directus-config/development/snapshot/fields/layers/item_presets.json +++ b/backend/directus-config/development/snapshot/fields/layers/item_presets.json @@ -18,7 +18,7 @@ }, "readonly": false, "required": false, - "sort": 16, + "sort": 17, "special": [ "cast-json" ], diff --git a/backend/directus-config/development/snapshot/fields/layers/notifications.json b/backend/directus-config/development/snapshot/fields/layers/notifications.json index 7d174468..b02104d2 100644 --- a/backend/directus-config/development/snapshot/fields/layers/notifications.json +++ b/backend/directus-config/development/snapshot/fields/layers/notifications.json @@ -15,7 +15,7 @@ "options": {}, "readonly": false, "required": false, - "sort": 17, + "sort": 18, "special": [ "m2m" ], diff --git a/backend/pull.sh b/backend/pull.sh new file mode 100755 index 00000000..53a92bf2 --- /dev/null +++ b/backend/pull.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +# base setup +SCRIPT_PATH=$(realpath $0) +SCRIPT_DIR=$(dirname $SCRIPT_PATH) + +DIRECTUS_URL="${DIRECTUS_URL:-http://localhost:8055}" +DIRECTUS_EMAIL="${DIRECTUS_EMAIL:-admin@it4c.dev}" +DIRECTUS_PASSWORD="${DIRECTUS_PASSWORD:-admin123}" + +PGPASSWORD="${PGPASSWORD:-'directus'}" +PGUSER="${PGUSER:-'directus'}" +PGDATABASE="${PGDATABASE:-'directus'}" + +PROJECT_NAME="${PROJECT:-development}" +PROJECT_FOLDER=$SCRIPT_DIR/directus-config/$PROJECT_NAME + +echo "Pull collections" +npx directus-sync@3.4.0 pull \ + --dump-path $PROJECT_FOLDER \ + --directus-url $DIRECTUS_URL \ + --directus-email $DIRECTUS_EMAIL \ + --directus-password $DIRECTUS_PASSWORD \ + || exit 1 diff --git a/backend/push.sh b/backend/push.sh new file mode 100755 index 00000000..aa535503 --- /dev/null +++ b/backend/push.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +# base setup +SCRIPT_PATH=$(realpath $0) +SCRIPT_DIR=$(dirname $SCRIPT_PATH) + +DIRECTUS_URL="${DIRECTUS_URL:-http://localhost:8055}" +DIRECTUS_EMAIL="${DIRECTUS_EMAIL:-admin@it4c.dev}" +DIRECTUS_PASSWORD="${DIRECTUS_PASSWORD:-admin123}" + +PGPASSWORD="${PGPASSWORD:-'directus'}" +PGUSER="${PGUSER:-'directus'}" +PGDATABASE="${PGDATABASE:-'directus'}" + +PROJECT_NAME="${PROJECT:-development}" +PROJECT_FOLDER=$SCRIPT_DIR/directus-config/$PROJECT_NAME + +echo "Push collections" +npx directus-sync@3.4.0 push \ + --dump-path $PROJECT_FOLDER \ + --directus-url $DIRECTUS_URL \ + --directus-email $DIRECTUS_EMAIL \ + --directus-password $DIRECTUS_PASSWORD \ + || exit 1 diff --git a/backend/seed.sh b/backend/seed.sh index d69219e0..b62384f6 100755 --- a/backend/seed.sh +++ b/backend/seed.sh @@ -15,14 +15,6 @@ PGDATABASE="${PGDATABASE:-'directus'}" PROJECT_NAME="${PROJECT:-development}" PROJECT_FOLDER=$SCRIPT_DIR/directus-config/$PROJECT_NAME -echo "Sync collections" -npx directus-sync@3.4.0 push \ - --dump-path $PROJECT_FOLDER \ - --directus-url $DIRECTUS_URL \ - --directus-email $DIRECTUS_EMAIL \ - --directus-password $DIRECTUS_PASSWORD \ - || exit 1 - echo "Seed data" npx directus-sync@3.4.0 seed push \ --seed-path $PROJECT_FOLDER/seed \ diff --git a/lib/src/Components/Item/PopupView.tsx b/lib/src/Components/Item/PopupView.tsx index 6eea6e43..7c096262 100644 --- a/lib/src/Components/Item/PopupView.tsx +++ b/lib/src/Components/Item/PopupView.tsx @@ -173,7 +173,7 @@ export const PopupView = ({ children }: { children?: React.ReactNode }) => { - {item.name} + {item.name || item.layer?.item_default_name} diff --git a/lib/src/Components/Map/Layer.tsx b/lib/src/Components/Map/Layer.tsx index 7239a7b3..48921443 100644 --- a/lib/src/Components/Map/Layer.tsx +++ b/lib/src/Components/Map/Layer.tsx @@ -35,6 +35,8 @@ export const Layer = ({ // eslint-disable-next-line camelcase public_edit_items, listed = true, + // eslint-disable-next-line camelcase + item_default_name = 'item', }: LayerProps) => { const setItemsApi = useSetItemsApi() const setItemsData = useSetItemsData() @@ -65,6 +67,8 @@ export const Layer = ({ // eslint-disable-next-line camelcase public_edit_items, listed, + // eslint-disable-next-line camelcase + item_default_name, }) api && setItemsApi({ @@ -86,6 +90,8 @@ export const Layer = ({ // eslint-disable-next-line camelcase public_edit_items, listed, + // eslint-disable-next-line camelcase + item_default_name, }) // eslint-disable-next-line react-hooks/exhaustive-deps }, [data, api]) diff --git a/lib/src/Components/Map/Subcomponents/ItemPopupComponents/HeaderView.tsx b/lib/src/Components/Map/Subcomponents/ItemPopupComponents/HeaderView.tsx index d31f3cd3..4d340157 100644 --- a/lib/src/Components/Map/Subcomponents/ItemPopupComponents/HeaderView.tsx +++ b/lib/src/Components/Map/Subcomponents/ItemPopupComponents/HeaderView.tsx @@ -60,7 +60,7 @@ export function HeaderView({ const avatar = (item?.image && appState.assetsApi.url + item.image + '?width=160&heigth=160') || item?.image_external - const title = item?.name + const title = item?.name ?? item?.layer?.item_default_name const subtitle = item?.subname const [address] = useState('') diff --git a/lib/src/Components/Map/Subcomponents/ItemPopupComponents/TextView.tsx b/lib/src/Components/Map/Subcomponents/ItemPopupComponents/TextView.tsx index 93fce564..03076bed 100644 --- a/lib/src/Components/Map/Subcomponents/ItemPopupComponents/TextView.tsx +++ b/lib/src/Components/Map/Subcomponents/ItemPopupComponents/TextView.tsx @@ -28,7 +28,7 @@ export const TextView = ({ }: { item?: Item itemId?: string - text?: string + text?: string | null truncate?: boolean rawText?: string }) => { @@ -44,7 +44,14 @@ export const TextView = ({ if (rawText) { innerText = replacedText = rawText - } else if (text) { + } else if (text === undefined) { + // Field was omitted by backend (no permission) + innerText = replacedText = `Login to see this ${item?.layer?.item_default_name ?? 'item'}` + } else if (text === null || text === '') { + // Field is not set or empty - show nothing + innerText = '' + } else { + // Field has a value innerText = text } diff --git a/lib/src/Components/Map/hooks/useItems.tsx b/lib/src/Components/Map/hooks/useItems.tsx index 0fb1af78..b15fdceb 100644 --- a/lib/src/Components/Map/hooks/useItems.tsx +++ b/lib/src/Components/Map/hooks/useItems.tsx @@ -5,9 +5,19 @@ /* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/no-misused-promises */ -import { useCallback, useReducer, createContext, useContext, useState } from 'react' +import { + useCallback, + useReducer, + createContext, + useContext, + useState, + useEffect, + useRef, +} from 'react' import { toast } from 'react-toastify' +import { useAuth } from '#components/Auth/useAuth' + import { useAddLayer } from './useLayers' import type { Item } from '#types/Item' @@ -18,6 +28,7 @@ type ActionType = | { type: 'UPDATE'; item: Item } | { type: 'REMOVE'; item: Item } | { type: 'RESET'; layer: LayerProps } + | { type: 'CLEAR_ALL' } type UseItemManagerResult = ReturnType @@ -43,8 +54,10 @@ function useItemsManager(initialItems: Item[]): { allItemsLoaded: boolean } { const addLayer = useAddLayer() + const { user } = useAuth() const [allItemsLoaded, setallItemsLoaded] = useState(false) + const layersRef = useRef([]) const [items, dispatch] = useReducer((state: Item[], action: ActionType) => { switch (action.type) { @@ -65,6 +78,8 @@ function useItemsManager(initialItems: Item[]): { return state.filter((item) => item !== action.item) case 'RESET': return state.filter((item) => item.layer?.name !== action.layer.name) + case 'CLEAR_ALL': + return [] default: throw new Error() } @@ -72,6 +87,7 @@ function useItemsManager(initialItems: Item[]): { const setItemsApi = useCallback(async (layer: LayerProps) => { addLayer(layer) + layersRef.current.push(layer) const result = await toast.promise(layer.api!.getItems(), { pending: `loading ${layer.name} ...`, success: `${layer.name} loaded`, @@ -127,6 +143,38 @@ function useItemsManager(initialItems: Item[]): { }) }, []) + const reloadAllItems = useCallback(async () => { + dispatch({ type: 'CLEAR_ALL' }) + setallItemsLoaded(false) + + for (const layer of layersRef.current) { + if (layer.api) { + const result = await toast.promise(layer.api.getItems(), { + pending: `loading ${layer.name} ...`, + success: `${layer.name} loaded`, + error: { + render({ data }) { + return `${data}` + }, + }, + }) + result.map((item) => { + dispatch({ type: 'ADD', item: { ...item, layer } }) + return null + }) + } + } + + setallItemsLoaded(true) + }, []) + + useEffect(() => { + if (layersRef.current.length > 0) { + void reloadAllItems() + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [user?.id]) + return { items, updateItem, diff --git a/lib/src/Components/Profile/Subcomponents/GalleryView.tsx b/lib/src/Components/Profile/Subcomponents/GalleryView.tsx index 18c0d4a3..4d8b7094 100644 --- a/lib/src/Components/Profile/Subcomponents/GalleryView.tsx +++ b/lib/src/Components/Profile/Subcomponents/GalleryView.tsx @@ -24,6 +24,7 @@ export const GalleryView = ({ item }: { item: Item }) => { const appState = useAppState() const images = item.gallery?.flatMap((g, index) => { + if (!g.directus_files_id) return [] const file = g.directus_files_id if (typeof file === 'string') return [] const { id, type, width, height } = file diff --git a/lib/src/Components/Profile/Subcomponents/ProfileTextView.tsx b/lib/src/Components/Profile/Subcomponents/ProfileTextView.tsx index 2adbb3e1..ea62c6d7 100644 --- a/lib/src/Components/Profile/Subcomponents/ProfileTextView.tsx +++ b/lib/src/Components/Profile/Subcomponents/ProfileTextView.tsx @@ -17,15 +17,14 @@ export const ProfileTextView = ({ }) => { const text = get(item, dataField) - const parsedText = typeof text !== 'string' ? '' : text + // undefined = no permission, null = not set, string = value exists + const shouldShowHeading = !(hideWhenEmpty && (text === '' || text === null)) return (
- {!(text === '' && hideWhenEmpty) && ( -

{heading}

- )} + {shouldShowHeading &&

{heading}

}
- +
) diff --git a/lib/src/types/LayerProps.d.ts b/lib/src/types/LayerProps.d.ts index 40aa6196..8fd0cf6e 100644 --- a/lib/src/types/LayerProps.d.ts +++ b/lib/src/types/LayerProps.d.ts @@ -25,4 +25,5 @@ export interface LayerProps { public_edit_items?: boolean listed?: boolean item_presets?: Record + item_default_name?: string }