mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
Merge branch 'main' into invites-2
This commit is contained in:
commit
1ffe86848a
9
.github/workflows/test.backend.seed.yml
vendored
9
.github/workflows/test.backend.seed.yml
vendored
@ -29,13 +29,18 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||
with:
|
||||
node-version-file: '.tool-versions'
|
||||
|
||||
- name: Build Docker Production
|
||||
run: |
|
||||
mkdir -p ./data/uploads
|
||||
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:
|
||||
@ -51,4 +56,4 @@ jobs:
|
||||
#
|
||||
# - name: Build Docker Development
|
||||
# run: docker compose build
|
||||
# working-directory: ${{env.WORKING_DIRECTORY}}
|
||||
# working-directory: ${{env.WORKING_DIRECTORY}}
|
||||
|
||||
211
.github/workflows/test.e2e.yml
vendored
Normal file
211
.github/workflows/test.e2e.yml
vendored
Normal file
@ -0,0 +1,211 @@
|
||||
name: test:e2e
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
cypress-e2e-tests:
|
||||
name: Run E2E Tests
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
tests-failed: ${{ steps.cypress-tests.outcome == 'failure' || steps.report-results.outputs.test_failed == 'true' }}
|
||||
tests-outcome: ${{ steps.cypress-tests.outcome }}
|
||||
test_failed: ${{ steps.report-results.outputs.test_failed }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@89d709d423dc495668cd762a18dd4a070611be3f # v5.0.0
|
||||
with:
|
||||
node-version-file: ./.tool-versions
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
app/package-lock.json
|
||||
lib/package-lock.json
|
||||
cypress/package-lock.json
|
||||
|
||||
- name: Build Library
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
working-directory: ./lib
|
||||
|
||||
- name: Build Frontend
|
||||
run: |
|
||||
cp .env.dist .env
|
||||
sed -i '/VITE_DIRECTUS_ADMIN_ROLE=/c\VITE_DIRECTUS_ADMIN_ROLE=8141dee8-8e10-48d0-baf1-680aea271298' .env
|
||||
npm ci
|
||||
npm run build
|
||||
working-directory: ./app
|
||||
|
||||
- name: Clean Database State
|
||||
run: |
|
||||
# Remove any existing database data to ensure fresh state
|
||||
sudo rm -rf ./data/database
|
||||
mkdir -p ./data/uploads
|
||||
sudo chmod 777 -R ./data
|
||||
|
||||
- name: Build and start all Containers
|
||||
run: docker compose up -d
|
||||
|
||||
- name: Wait for Directus to be Ready
|
||||
run: |
|
||||
echo "Waiting for Directus API to be ready..."
|
||||
timeout 120 bash -c 'until curl -f http://localhost:8055/server/health; do echo "Waiting for Directus..."; sleep 5; done'
|
||||
echo "Directus is ready!"
|
||||
|
||||
- name: Seed Backend
|
||||
run: |
|
||||
mkdir -p ./data/uploads
|
||||
sudo chmod 777 -R ./data
|
||||
cd backend && ./push.sh && ./seed.sh
|
||||
working-directory: ./
|
||||
|
||||
- name: Wait for Application to be Ready
|
||||
run: |
|
||||
echo "Waiting for application to be ready..."
|
||||
timeout 300 bash -c 'until curl -f http://localhost:8080/login; do sleep 5; done'
|
||||
echo "Application is ready!"
|
||||
|
||||
- name: Health Check
|
||||
run: |
|
||||
echo "Frontend health check:"
|
||||
curl -f http://localhost:8080/login || exit 1
|
||||
echo "Backend health check:"
|
||||
curl -f http://localhost:8055/server/health || exit 1
|
||||
|
||||
- name: Install Cypress Dependencies
|
||||
run: npm ci
|
||||
working-directory: ./cypress
|
||||
|
||||
- name: Setup Display Environment for Parallel Tests
|
||||
run: |
|
||||
echo "Setting up display environment for parallel Cypress execution..."
|
||||
|
||||
# Kill any existing Xvfb processes to ensure clean state
|
||||
sudo pkill Xvfb || true
|
||||
|
||||
# Remove any existing lock files
|
||||
sudo rm -f /tmp/.X*-lock || true
|
||||
|
||||
# Ensure xvfb is available
|
||||
which xvfb-run || (sudo apt-get update && sudo apt-get install -y xvfb)
|
||||
|
||||
echo "Display environment setup complete"
|
||||
|
||||
- name: Run E2E Tests
|
||||
id: cypress-tests
|
||||
run: |
|
||||
# Override the npm script to use xvfb-run with display isolation
|
||||
SPEC_COUNT=$(find e2e -name "*.cy.ts" | wc -l)
|
||||
echo "Running $SPEC_COUNT test chunks in parallel with display isolation"
|
||||
|
||||
# Array to store background process PIDs
|
||||
declare -a pids=()
|
||||
|
||||
# Launch parallel processes with isolated displays
|
||||
for i in $(seq 0 $((SPEC_COUNT-1))); do
|
||||
echo "Starting Cypress chunk $((i + 1))/$SPEC_COUNT on display :$((100 + i))"
|
||||
(
|
||||
SPLIT="$SPEC_COUNT" SPLIT_INDEX="$i" SPLIT_SUMMARY=false \
|
||||
xvfb-run --server-num="$((100 + i))" \
|
||||
--server-args="-screen 0 1280x720x24 -ac +extension GLX +render -noreset" \
|
||||
npx cypress run --e2e --browser chromium
|
||||
) &
|
||||
pids+=($!)
|
||||
done
|
||||
|
||||
# Wait for all background processes and collect exit codes
|
||||
exit_code=0
|
||||
for pid in "${pids[@]}"; do
|
||||
if ! wait "$pid"; then
|
||||
echo "Process $pid failed"
|
||||
exit_code=1
|
||||
fi
|
||||
done
|
||||
|
||||
echo "All parallel test processes completed"
|
||||
|
||||
# Exit with failure if any test failed
|
||||
if [ $exit_code -ne 0 ]; then
|
||||
echo "❌ Some tests failed"
|
||||
exit 1
|
||||
else
|
||||
echo "✅ All tests passed"
|
||||
fi
|
||||
working-directory: ./cypress
|
||||
env:
|
||||
# Disable individual cypress-split summaries to avoid conflicts
|
||||
SPLIT_SUMMARY: false
|
||||
|
||||
- name: Upload test artifacts on failure
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@2848b2cda0e5190984587ec6bb1f36730ca78d50 # v4.6.2
|
||||
with:
|
||||
name: cypress-test-results-${{ github.run_id }}
|
||||
path: |
|
||||
cypress/results/
|
||||
cypress/screenshots/
|
||||
retention-days: 7
|
||||
if-no-files-found: warn
|
||||
|
||||
process-test-reports:
|
||||
name: Process Test Reports
|
||||
runs-on: ubuntu-latest
|
||||
needs: cypress-e2e-tests
|
||||
if: failure() && needs.cypress-e2e-tests.result == 'failure'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@89d709d423dc495668cd762a18dd4a070611be3f # v5.0.0
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: cypress/package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
working-directory: ./cypress
|
||||
|
||||
- name: Download test artifacts
|
||||
uses: actions/download-artifact@4a24838f3d5601fd639834081e118c2995d51e1c # v5.0.0
|
||||
with:
|
||||
name: cypress-test-results-${{ github.run_id }}
|
||||
path: ./cypress
|
||||
|
||||
- name: Merge JSON reports into one consolidated report
|
||||
run: ./scripts/merge-reports.sh
|
||||
working-directory: ./cypress
|
||||
|
||||
- name: Generate HTML report with screenshots
|
||||
run: ./scripts/generate-html-report.sh
|
||||
working-directory: ./cypress
|
||||
|
||||
- name: Create simple index page
|
||||
run: ./scripts/create-index-page.sh
|
||||
working-directory: ./cypress
|
||||
env:
|
||||
GITHUB_RUN_ID: ${{ github.run_id }}
|
||||
GITHUB_SHA: ${{ github.sha }}
|
||||
|
||||
- name: Upload consolidated test report
|
||||
uses: actions/upload-artifact@2848b2cda0e5190984587ec6bb1f36730ca78d50 # v4.6.2
|
||||
with:
|
||||
name: e2e-test-report-${{ github.run_id }}
|
||||
path: cypress/results/html/
|
||||
retention-days: 14
|
||||
if-no-files-found: warn
|
||||
|
||||
- name: Upload raw test data (for debugging)
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@2848b2cda0e5190984587ec6bb1f36730ca78d50 # v4.6.2
|
||||
with:
|
||||
name: e2e-raw-data-${{ github.run_id }}
|
||||
path: |
|
||||
cypress/results/
|
||||
cypress/screenshots/
|
||||
cypress/videos/
|
||||
retention-days: 7
|
||||
if-no-files-found: warn
|
||||
34
.github/workflows/test.lint.cypress.yml
vendored
Normal file
34
.github/workflows/test.lint.cypress.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
name: test:lint:cypress
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Detect File Changes - lint - cypress
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
cypress: ${{ steps.filter.outputs.cypress }}
|
||||
steps:
|
||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
|
||||
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
cypress:
|
||||
- '.github/workflows/test.lint.cypress.yml'
|
||||
- '.github/workflows/test.e2e.yml'
|
||||
- 'cypress/**/*'
|
||||
|
||||
lint:
|
||||
if: needs.files-changed.outputs.cypress == 'true'
|
||||
name: Lint - cypress
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
|
||||
- uses: actions/setup-node@89d709d423dc495668cd762a18dd4a070611be3f # v5.0.0
|
||||
with:
|
||||
node-version-file: '.tool-versions'
|
||||
- name: Lint
|
||||
run: npm install && npm run lint
|
||||
working-directory: cypress/
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,2 +1,7 @@
|
||||
.claude/
|
||||
data/
|
||||
node_modules/
|
||||
cypress/node_modules/
|
||||
cypress/results/
|
||||
cypress/runner-results/
|
||||
cypress/screenshots/
|
||||
|
||||
@ -1 +1 @@
|
||||
nodejs 20.12.1
|
||||
nodejs 22.20.0
|
||||
|
||||
@ -37,11 +37,6 @@ npm run test:unit:dev # Run Vitest in watch mode
|
||||
npm run docs:generate # Generate TypeDoc documentation
|
||||
```
|
||||
|
||||
### Root Level
|
||||
```bash
|
||||
./scripts/check-lint.sh # Run linting on both app and lib (used by PR hooks)
|
||||
```
|
||||
|
||||
### Backend (Directus)
|
||||
```bash
|
||||
cd app
|
||||
@ -94,9 +89,9 @@ npx directus-sync push --directus-url http://localhost:8055 --directus-email adm
|
||||
### Testing Strategy
|
||||
|
||||
- **Unit Tests**: Vitest for lib components with coverage reporting
|
||||
- **Component Tests**: Cypress for React component integration
|
||||
- **Linting**: ESLint with TypeScript rules for code quality
|
||||
- **Type Checking**: TypeScript strict mode across all packages
|
||||
- **End-to-End Tests**: Cypress for testing the app's UI and user flows
|
||||
|
||||
### Import Conventions
|
||||
|
||||
|
||||
17
app/package-lock.json
generated
17
app/package-lock.json
generated
@ -50,6 +50,9 @@
|
||||
"typescript": "^5.0.2",
|
||||
"vite": "^6.2.0",
|
||||
"vite-plugin-pwa": "^0.21.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
@ -4517,13 +4520,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz",
|
||||
"integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==",
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz",
|
||||
"integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
"form-data": "^4.0.4",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
@ -11545,9 +11548,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "6.3.5",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
|
||||
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
|
||||
"version": "6.3.6",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz",
|
||||
"integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
|
||||
@ -3,6 +3,9 @@
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=22.20.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
"build": "tsc && vite build",
|
||||
|
||||
@ -118,7 +118,7 @@ function App() {
|
||||
setError(
|
||||
typeof error === 'string'
|
||||
? error
|
||||
: error?.errors?.[0]?.message ||
|
||||
: (error?.errors?.length > 0 ? error.errors[0]?.message : null) ||
|
||||
error?.message ||
|
||||
'Failed to connect to the server. Please check your connection and try again.',
|
||||
)
|
||||
@ -150,6 +150,7 @@ function App() {
|
||||
/>
|
||||
),
|
||||
name: l.name, // name that appear in Sidebar
|
||||
color: l.menuColor,
|
||||
})),
|
||||
)
|
||||
// eslint-disable-next-line no-catch-all/no-catch-all
|
||||
@ -159,7 +160,7 @@ function App() {
|
||||
setError(
|
||||
typeof error === 'string'
|
||||
? error
|
||||
: error?.errors?.[0]?.message ||
|
||||
: (error?.errors?.length > 0 ? error.errors[0]?.message : null) ||
|
||||
error?.message ||
|
||||
'Failed to load map layers. Please check your permissions and try again.',
|
||||
)
|
||||
|
||||
@ -26,7 +26,7 @@ export class mapApi {
|
||||
else return 'null'
|
||||
} catch (error: any) {
|
||||
console.log(error)
|
||||
if (error.errors[0]?.message) throw error.errors[0].message
|
||||
if (error.errors?.length > 0 && error.errors[0]?.message) throw error.errors[0].message
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,6 +96,11 @@ function MapContainer({
|
||||
tileServerUrl={map.tile_server_url}
|
||||
tileServerAttribution={map.tile_server_attribution}
|
||||
inviteApi={inviteApi}
|
||||
tilesType={map.tiles_type}
|
||||
maplibreStyle={map.maplibre_style}
|
||||
showFullscreenControl={map.show_fullscreen_control}
|
||||
zoomOffset={map.zoom_offset}
|
||||
tileSize={map.tile_size}
|
||||
>
|
||||
{layers &&
|
||||
apis &&
|
||||
@ -119,6 +124,7 @@ function MapContainer({
|
||||
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}
|
||||
>
|
||||
<PopupView>
|
||||
{layer.itemType.show_start_end && <StartEndView></StartEndView>}
|
||||
@ -128,7 +134,7 @@ function MapContainer({
|
||||
parameterField={
|
||||
layer.itemType.custom_profile_url ? 'extended.external_profile_id' : 'id'
|
||||
}
|
||||
text={layer.itemType.botton_label ?? 'Profile'}
|
||||
text={layer.itemType.button_label ?? 'Profile'}
|
||||
target={layer.itemType.custom_profile_url ? '_blank' : '_self'}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -20,19 +20,27 @@ export default defineConfig({
|
||||
*/
|
||||
},
|
||||
plugins: [react(), tailwindcss(), tsConfigPaths()],
|
||||
resolve: {
|
||||
dedupe: ['react', 'react-dom', 'react-router-dom'],
|
||||
},
|
||||
build: {
|
||||
sourcemap: true,
|
||||
modulePreload: {
|
||||
// Don't preload maplibre chunks - only load when actually needed
|
||||
resolveDependencies: (_filename, deps) => {
|
||||
return deps.filter((dep) => !dep.includes('maplibre'))
|
||||
},
|
||||
},
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: (id) => {
|
||||
if (id.includes('lib/src')) {
|
||||
// Handle lib source (dev) or dist (prod)
|
||||
if (id.includes('lib/src') || id.includes('lib/dist')) {
|
||||
// Separate chunk for MapLibre components
|
||||
if (id.includes('MapLibre')) {
|
||||
return 'maplibre-layer'
|
||||
}
|
||||
return 'utopia-ui'
|
||||
}
|
||||
if (id.includes('node_modules')) {
|
||||
if (id.includes('react')) {
|
||||
if (id.includes('react') || id.includes('scheduler') || id.includes('use-sync-external-store')) {
|
||||
return 'react'
|
||||
}
|
||||
if (id.includes('tiptap')) {
|
||||
@ -41,9 +49,11 @@ export default defineConfig({
|
||||
if (id.includes('leaflet')) {
|
||||
return 'leaflet'
|
||||
}
|
||||
if (id.includes('lib/node_modules')) {
|
||||
return 'utopia-ui-vendor'
|
||||
} else return 'vendor'
|
||||
// Separate chunk for maplibre-gl
|
||||
if (id.includes('maplibre-gl')) {
|
||||
return 'maplibre-gl'
|
||||
}
|
||||
return 'vendor'
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
FROM node:20-alpine as third-party-ext
|
||||
FROM node:22.20.0-alpine as third-party-ext
|
||||
RUN apk add python3 g++ make
|
||||
WORKDIR /extensions
|
||||
ADD extensions .
|
||||
RUN npm install
|
||||
# Move all extensions the starts with directus-extension-, using find, to the /extensions/directus folder
|
||||
# Move all extensions that start with directus-extension-, using find, to the /extensions/directus folder
|
||||
RUN mkdir -p ./directus
|
||||
RUN cd node_modules && find . -maxdepth 1 -type d -name "directus-extension-*" -exec mv {} ../directus \;
|
||||
|
||||
FROM directus/directus:11.7.2
|
||||
FROM directus/directus:11.9.3
|
||||
# Copy third party extensions
|
||||
COPY --from=third-party-ext /extensions/directus ./extensions
|
||||
@ -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`.
|
||||
|
||||
1
backend/directus-config/.gitignore
vendored
1
backend/directus-config/.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
/*
|
||||
!development/
|
||||
**/specs/
|
||||
!.gitignore
|
||||
@ -1649,7 +1649,7 @@
|
||||
"permissions": null,
|
||||
"validation": null,
|
||||
"presets": {
|
||||
"role": "de2f2f91-0d78-4691-b01b-589e1345109b"
|
||||
"role": "72f08162-fbd4-432e-b3f2-c8a6ecb289fc"
|
||||
},
|
||||
"fields": [
|
||||
"first_name",
|
||||
@ -2205,102 +2205,6 @@
|
||||
"policy": "4d5d2bd8-7e1f-40c1-b10b-3f0ecac70877",
|
||||
"_syncId": "8e8dcb5f-e3df-4d10-a44b-ac916bed0567"
|
||||
},
|
||||
{
|
||||
"collection": "oceannomads_events",
|
||||
"action": "create",
|
||||
"permissions": null,
|
||||
"validation": null,
|
||||
"presets": null,
|
||||
"fields": [
|
||||
"*"
|
||||
],
|
||||
"policy": "b0eb656b-96e5-4a30-a083-6ef8141e6a4c",
|
||||
"_syncId": "c20b5c36-b03c-4a4d-8a86-0c5fc5805bbb"
|
||||
},
|
||||
{
|
||||
"collection": "oceannomads_events",
|
||||
"action": "delete",
|
||||
"permissions": null,
|
||||
"validation": null,
|
||||
"presets": null,
|
||||
"fields": [
|
||||
"*"
|
||||
],
|
||||
"policy": "b0eb656b-96e5-4a30-a083-6ef8141e6a4c",
|
||||
"_syncId": "e61f5cdf-33e2-46db-828b-aab05c30dd32"
|
||||
},
|
||||
{
|
||||
"collection": "oceannomads_events",
|
||||
"action": "read",
|
||||
"permissions": null,
|
||||
"validation": null,
|
||||
"presets": null,
|
||||
"fields": [
|
||||
"*"
|
||||
],
|
||||
"policy": "b0eb656b-96e5-4a30-a083-6ef8141e6a4c",
|
||||
"_syncId": "c86a309a-d4ec-4135-bff4-238825ea7053"
|
||||
},
|
||||
{
|
||||
"collection": "oceannomads_events",
|
||||
"action": "update",
|
||||
"permissions": null,
|
||||
"validation": null,
|
||||
"presets": null,
|
||||
"fields": [
|
||||
"*"
|
||||
],
|
||||
"policy": "b0eb656b-96e5-4a30-a083-6ef8141e6a4c",
|
||||
"_syncId": "d03d2704-3311-4dc1-bb94-b542c89f94b4"
|
||||
},
|
||||
{
|
||||
"collection": "oceannomads_profiles",
|
||||
"action": "create",
|
||||
"permissions": null,
|
||||
"validation": null,
|
||||
"presets": null,
|
||||
"fields": [
|
||||
"*"
|
||||
],
|
||||
"policy": "b0eb656b-96e5-4a30-a083-6ef8141e6a4c",
|
||||
"_syncId": "5a2ba99b-bd06-4a47-8fc1-3919001d1c4a"
|
||||
},
|
||||
{
|
||||
"collection": "oceannomads_profiles",
|
||||
"action": "delete",
|
||||
"permissions": null,
|
||||
"validation": null,
|
||||
"presets": null,
|
||||
"fields": [
|
||||
"*"
|
||||
],
|
||||
"policy": "b0eb656b-96e5-4a30-a083-6ef8141e6a4c",
|
||||
"_syncId": "47783f7d-cb09-4dbd-a2ad-cf26c3c02192"
|
||||
},
|
||||
{
|
||||
"collection": "oceannomads_profiles",
|
||||
"action": "read",
|
||||
"permissions": null,
|
||||
"validation": null,
|
||||
"presets": null,
|
||||
"fields": [
|
||||
"*"
|
||||
],
|
||||
"policy": "b0eb656b-96e5-4a30-a083-6ef8141e6a4c",
|
||||
"_syncId": "3f0621ff-34ba-4f6c-9274-b5fb1ccf8c3a"
|
||||
},
|
||||
{
|
||||
"collection": "oceannomads_profiles",
|
||||
"action": "update",
|
||||
"permissions": null,
|
||||
"validation": null,
|
||||
"presets": null,
|
||||
"fields": [
|
||||
"*"
|
||||
],
|
||||
"policy": "b0eb656b-96e5-4a30-a083-6ef8141e6a4c",
|
||||
"_syncId": "dc87ced0-4465-4e68-8732-97d73b0d2de4"
|
||||
},
|
||||
{
|
||||
"collection": "attestations_directus_users",
|
||||
"action": "create",
|
||||
|
||||
@ -23,20 +23,7 @@
|
||||
"enforce_tfa": false,
|
||||
"admin_access": false,
|
||||
"app_access": true,
|
||||
"roles": [
|
||||
{
|
||||
"role": null,
|
||||
"sort": 1
|
||||
},
|
||||
{
|
||||
"role": null,
|
||||
"sort": 1
|
||||
},
|
||||
{
|
||||
"role": null,
|
||||
"sort": null
|
||||
}
|
||||
],
|
||||
"roles": [],
|
||||
"_syncId": "_sync_default_public_policy"
|
||||
},
|
||||
{
|
||||
@ -79,12 +66,7 @@
|
||||
"enforce_tfa": false,
|
||||
"admin_access": false,
|
||||
"app_access": false,
|
||||
"roles": [
|
||||
{
|
||||
"role": null,
|
||||
"sort": null
|
||||
}
|
||||
],
|
||||
"roles": [],
|
||||
"_syncId": "b0eb656b-96e5-4a30-a083-6ef8141e6a4c"
|
||||
},
|
||||
{
|
||||
|
||||
@ -60,6 +60,8 @@
|
||||
"public_registration_role": null,
|
||||
"public_registration_email_filter": null,
|
||||
"visual_editor_urls": null,
|
||||
"accepted_terms": true,
|
||||
"project_id": "0199aa52-4dd7-7293-984a-f2af93b5f8fd",
|
||||
"_syncId": "55f04445-0c26-4201-ab9c-d6e0fbadf6bf"
|
||||
}
|
||||
]
|
||||
|
||||
49
backend/directus-config/development/seed/Themes.json
Normal file
49
backend/directus-config/development/seed/Themes.json
Normal file
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -17,16 +17,6 @@
|
||||
"title": "Utopia Logo",
|
||||
"tags": [],
|
||||
"description": "Utopia Logo"
|
||||
},
|
||||
{
|
||||
"_sync_id": "vessel-svg",
|
||||
"_file_path": "./files/vessel.svg",
|
||||
"storage": "local",
|
||||
"folder": "27b2a288-d50a-48b7-88cd-35945503277b",
|
||||
"filename_download": "vessel.svg",
|
||||
"title": "Vessel SVG",
|
||||
"tags": [],
|
||||
"description": "Vessel SVG"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="666px" height="541px" viewBox="0 0 6660 5410" preserveAspectRatio="xMidYMid meet">
|
||||
<g id="layer101" fill="#000000" stroke="none">
|
||||
</g>
|
||||
<g id="layer102" fill="#182d45" stroke="none">
|
||||
<path d="M2600 5360 c-58 -5 -242 -8 -410 -8 -168 0 -316 -2 -330 -5 -14 -4 -81 -11 -150 -17 -125 -11 -369 -43 -445 -60 -22 -5 -82 -13 -134 -20 -172 -20 -424 -141 -607 -291 -159 -131 -215 -184 -288 -270 -105 -126 -138 -192 -176 -357 -26 -112 -29 -139 -20 -172 13 -48 30 -66 78 -81 55 -17 2639 -31 4715 -26 2008 5 1780 -11 1785 125 7 204 -81 382 -303 611 -119 123 -228 214 -315 263 -66 38 -193 81 -375 128 -185 49 -240 53 -863 69 -204 5 -376 13 -382 16 -5 3 -128 10 -272 16 -145 5 -375 14 -513 19 -137 5 -302 16 -365 24 -285 36 -483 47 -630 36z"/>
|
||||
<path d="M1025 3861 c-75 -10 -141 -39 -180 -80 -27 -30 -30 -38 -29 -99 0 -81 21 -130 253 -597 171 -344 251 -490 353 -643 87 -129 163 -274 224 -422 58 -144 73 -173 145 -290 94 -154 161 -204 215 -159 40 33 45 71 44 319 0 132 4 301 9 377 12 163 14 329 13 868 -1 230 3 440 9 495 14 124 2 166 -56 195 -34 17 -70 21 -250 27 -348 13 -686 17 -750 9z"/>
|
||||
<path d="M4653 3856 c-31 -9 -78 -49 -96 -81 -9 -15 -12 -174 -13 -585 0 -574 -17 -1381 -29 -1400 -12 -20 16 -602 30 -637 8 -18 29 -42 47 -53 67 -40 129 -17 169 63 20 40 34 63 132 217 63 100 173 307 323 610 75 151 151 297 170 323 19 26 44 71 55 100 11 29 47 102 79 162 32 61 79 157 103 215 83 192 112 255 134 290 12 19 33 64 46 100 14 36 53 130 87 210 34 80 73 171 87 203 18 44 23 68 18 97 -8 48 -20 63 -70 91 -91 51 -93 51 -283 60 -103 5 -227 13 -277 19 -106 12 -673 9 -712 -4z"/>
|
||||
<path d="M2412 3831 c-137 -6 -145 -7 -164 -31 -15 -18 -19 -34 -15 -60 27 -171 49 -238 144 -440 30 -63 66 -148 79 -188 13 -39 42 -109 65 -154 23 -45 66 -139 95 -208 59 -136 131 -270 259 -480 45 -74 116 -196 158 -270 41 -74 123 -207 182 -295 59 -88 123 -191 142 -230 20 -38 79 -137 133 -220 53 -82 115 -182 138 -222 23 -40 75 -116 115 -170 129 -174 268 -394 364 -573 92 -173 150 -250 189 -250 27 0 91 72 103 115 17 63 14 520 -5 683 -14 122 -15 175 -3 492 7 195 14 661 15 1035 0 374 4 802 8 951 8 312 4 346 -52 395 -20 17 -50 37 -67 45 -35 14 -457 34 -739 34 -94 0 -220 4 -280 10 -324 29 -650 41 -864 31z"/>
|
||||
</g>
|
||||
<g id="layer103" fill="#37949a" stroke="none">
|
||||
<path d="M1950 5193 c-8 -1 -96 -9 -195 -18 -163 -14 -462 -52 -475 -60 -3 -2 -50 -8 -105 -15 -115 -14 -190 -40 -324 -112 -115 -62 -204 -123 -280 -192 -31 -29 -80 -69 -108 -90 -29 -21 -53 -41 -53 -44 0 -4 -26 -36 -59 -72 -64 -72 -115 -156 -126 -210 -4 -19 -20 -68 -36 -108 -16 -40 -29 -74 -29 -75 0 -1 1317 -3 2928 -4 1610 -1 3032 -5 3161 -8 l234 -7 -6 34 c-36 190 -116 317 -339 533 -172 168 -263 221 -433 251 -43 7 -103 21 -132 29 -129 38 -291 50 -783 60 -146 3 -346 9 -445 15 -336 18 -640 30 -755 30 -64 0 -170 7 -235 15 -342 42 -444 48 -920 49 -259 1 -478 0 -485 -1z"/>
|
||||
</g>
|
||||
<g id="layer104" fill="#f9d400" stroke="none">
|
||||
<path d="M3515 379 c-97 -43 -174 -69 -201 -69 -13 0 -58 17 -99 37 -60 30 -79 35 -93 26 -28 -18 -20 -76 21 -137 41 -61 82 -95 145 -119 60 -24 101 -21 158 9 155 84 178 94 214 94 24 0 92 -22 174 -55 148 -60 187 -67 210 -34 22 32 6 60 -55 95 -30 16 -83 52 -119 78 -149 111 -232 129 -355 75z"/>
|
||||
</g>
|
||||
|
||||
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.2 KiB |
@ -16,6 +16,10 @@
|
||||
{
|
||||
"_sync_id": "b0c52d6e-b3d2-4e3b-89e2-065be324e27b",
|
||||
"hideInputLabel": false
|
||||
},
|
||||
{
|
||||
"_sync_id": "6d18b616-6f4f-4987-9860-681b88bdc068",
|
||||
"hideInputLabel": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -13,7 +13,7 @@
|
||||
"_sync_id": "item-places-1",
|
||||
"name": "Welcome to Utopia Map",
|
||||
"subname" : "The opensource collaborative mapping plattform",
|
||||
"text": "Check out our [GitHub](https://github.com/utopia-os/utopia-map)!",
|
||||
"text": "The Utopia Map is a flexible collaborative app for decentralized coordination and real-life networking that can be adapted to the specific requirements of different networks. Its central element is the interactive geographical map, where users can add and manage **Items** in predefined **Layers**. \n\nUtopia Map is made for networks and initiatives that aim to connect people in real life. By providing a custom instance of Utopia Map, each network can grow and coordinate its ecosystem effectively while encouraging real-world interactions and collaborations.\n\n## Key Features\n- **Interactive Map**: The core feature is an intuitive geographical map where users can add, edit, and manage items like members, activities, and resources. Each map instance has its own identity, users, and unique configuration.\n- **Customizable Layers**: Items are organized into predefined Layers, each with specific icons, colors, texts, and Map Markers. This ensures clarity and relevance for different networks.\n- **Dynamic Map Markers**: Geographic position of item are indicated on the map by adaptive and customizable Map Markers\n- **Popups**: Clicking a Map Marker reveals a Popup — a compact preview of the Item with its most relevant information. Define custom Popups for each of your Layers.\n- **Profiles**: Each Item has a dedicated Profile that showcases all its associated data, making it easier to explore and manage. Define custom profiles for each of your Layers.\n\nCheck out our [GitHub](https://github.com/utopia-os/utopia-map)!",
|
||||
"position": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
@ -21,13 +21,13 @@
|
||||
50.51565268622562
|
||||
]
|
||||
},
|
||||
"image": "utopia-logo",
|
||||
"layer" : "layer-places"
|
||||
},
|
||||
{
|
||||
"_sync_id": "item-event-1",
|
||||
"name": "Some Event",
|
||||
"subname" : "The opensource collaborative mapping plattform",
|
||||
"text": "Check out our [GitHub](https://github.com/utopia-os/utopia-map)!",
|
||||
"text": "This is an example event. Events are temporary items that disappear once the end date has passed.",
|
||||
"position": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
@ -40,9 +40,9 @@
|
||||
"end": "2027-06-25T12:00:00"
|
||||
},
|
||||
{
|
||||
"_sync_id": "item-nomad-location-1",
|
||||
"name": "Anton Tranelis",
|
||||
"text": "bla blab ...",
|
||||
"_sync_id": "item-user-1",
|
||||
"name": "Admin User",
|
||||
"text": "This is the personal profile of the admin user. Every user can place his personal profile on the map.",
|
||||
"position": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
@ -50,46 +50,134 @@
|
||||
51.41565268622562
|
||||
]
|
||||
},
|
||||
"layer" : "layer-nomads_location"
|
||||
"layer" : "layer-people"
|
||||
},
|
||||
{
|
||||
"_sync_id": "item-nomad-base-1",
|
||||
"name": "Anton Tranelis",
|
||||
"text": "bla blab ...",
|
||||
"_sync_id": "item-places-2",
|
||||
"name": "Community Garden Berlin",
|
||||
"subname": "Organic vegetables and sustainable farming",
|
||||
"text": "A thriving community garden in Berlin where neighbors grow organic vegetables together. We focus on #sustainability #community #gardening #organic practices. Join us for weekly workshops on #permaculture and #composting!",
|
||||
"position": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
9.67625824315172,
|
||||
48.41565268622562
|
||||
13.404954,
|
||||
52.520008
|
||||
]
|
||||
},
|
||||
"layer" : "layer-nomads_base"
|
||||
"layer" : "layer-places"
|
||||
},
|
||||
{
|
||||
"_sync_id": "item-vessel-1",
|
||||
"name": "Vessel XY",
|
||||
"text": "shipping the sea",
|
||||
"_sync_id": "item-event-2",
|
||||
"name": "Tech Meetup Munich",
|
||||
"text": "Monthly #technology #networking event for developers and tech enthusiasts. Topics include #javascript #opensource #webdev and #innovation. Great for #learning and meeting like-minded people!",
|
||||
"position": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
-2.67625824315172,
|
||||
48.61565268622562
|
||||
11.576124,
|
||||
48.137154
|
||||
]
|
||||
},
|
||||
"layer" : "layer-vessels"
|
||||
"layer" : "layer-events",
|
||||
"start": "2025-12-15T18:00:00",
|
||||
"end": "2025-12-15T21:00:00"
|
||||
},
|
||||
{
|
||||
"_sync_id": "item-basecamp-1",
|
||||
"name": "Basecamp XY",
|
||||
"text": "come and join our camp",
|
||||
{
|
||||
"_sync_id": "item-people-2",
|
||||
"name": "Jane Developer",
|
||||
"text": "Full-stack developer passionate about #opensource projects and #javascript frameworks. Interested in #sustainability tech and #community building. Always happy to collaborate on #innovation projects!",
|
||||
"position": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
1.6007423400878908,
|
||||
50.184428095190555
|
||||
2.349014,
|
||||
48.864716
|
||||
]
|
||||
},
|
||||
"layer" : "layer-basecamps"
|
||||
"layer" : "layer-people"
|
||||
},
|
||||
{
|
||||
"_sync_id": "item-places-3",
|
||||
"name": "Café Collaboration London",
|
||||
"subname": "Co-working space and coffee shop",
|
||||
"text": "A vibrant co-working café in London perfect for #networking and #collaboration. Features excellent coffee, fast wifi, and a #community of freelancers and entrepreneurs. Regular #events and workshops on #business and #creativity.",
|
||||
"position": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
-0.127758,
|
||||
51.507351
|
||||
]
|
||||
},
|
||||
"layer" : "layer-places"
|
||||
},
|
||||
{
|
||||
"_sync_id": "item-event-3",
|
||||
"name": "Sustainability Workshop NYC",
|
||||
"text": "Learn about #sustainability practices and #environmental solutions in this hands-on workshop. Topics include #recycling #renewable energy and #green living. Perfect for #activists and #eco enthusiasts!",
|
||||
"position": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
-74.005941,
|
||||
40.712784
|
||||
]
|
||||
},
|
||||
"layer" : "layer-events",
|
||||
"start": "2025-11-20T14:00:00",
|
||||
"end": "2025-11-20T17:00:00"
|
||||
},
|
||||
{
|
||||
"_sync_id": "item-people-3",
|
||||
"name": "Alex Entrepreneur",
|
||||
"text": "Serial entrepreneur focused on #sustainability and #innovation. Building #community-driven solutions for #environmental challenges. Always looking for #collaboration opportunities in #greentech!",
|
||||
"position": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
-122.419416,
|
||||
37.774929
|
||||
]
|
||||
},
|
||||
"layer" : "layer-people"
|
||||
},
|
||||
{
|
||||
"_sync_id": "item-places-4",
|
||||
"name": "Makerspace Tokyo",
|
||||
"subname": "Innovation hub for creators",
|
||||
"text": "State-of-the-art makerspace in Tokyo with 3D printers, laser cutters, and electronics lab. Perfect for #makers #innovation #prototyping and #learning. Join our #community of inventors and #entrepreneurs!",
|
||||
"position": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
139.691706,
|
||||
35.689487
|
||||
]
|
||||
},
|
||||
"layer" : "layer-places"
|
||||
},
|
||||
{
|
||||
"_sync_id": "item-event-4",
|
||||
"name": "Open Source Conference",
|
||||
"text": "Annual conference celebrating #opensource software and #collaboration. Features talks on #javascript #python #webdev and #community building. Great for #developers #learning and #networking!",
|
||||
"position": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
-0.118092,
|
||||
51.509865
|
||||
]
|
||||
},
|
||||
"layer" : "layer-events",
|
||||
"start": "2025-10-10T09:00:00",
|
||||
"end": "2025-10-12T18:00:00"
|
||||
},
|
||||
{
|
||||
"_sync_id": "item-places-5",
|
||||
"name": "Test & Special Characters!",
|
||||
"subname": "Edge case for search testing",
|
||||
"text": "This item tests special characters: @#$%^&*()_+ and unicode: café naïve résumé. Also tests very long content that might affect search performance and display. Contains #testing #edgecase #unicode #special-chars tags.",
|
||||
"position": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
4.902257,
|
||||
52.367573
|
||||
]
|
||||
},
|
||||
"layer" : "layer-places"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -12,72 +12,13 @@
|
||||
{
|
||||
"_sync_id": "layer-places",
|
||||
"name": "Places",
|
||||
"itemType": "type-simple",
|
||||
"itemType": "type-text-gallery",
|
||||
"userProfileLayer": false,
|
||||
"indexIcon": "map-pin-outline",
|
||||
"menuColor": "#2ECDA7",
|
||||
"menuIcon": "point-solid",
|
||||
"menuColor": "#008e5b",
|
||||
"menuText": "Add new Place",
|
||||
"markerIcon" : "marker-point",
|
||||
"markerShape" : "circle",
|
||||
"markerDefaultColor2": null,
|
||||
"onlyOnePerOwner": false,
|
||||
"index_plus_button": true,
|
||||
"public_edit_items": false,
|
||||
"listed": true,
|
||||
"item_presets": null,
|
||||
"sort": 1
|
||||
},
|
||||
{
|
||||
"_sync_id": "layer-events",
|
||||
"name": "Events",
|
||||
"itemType": "type-event",
|
||||
"userProfileLayer": false,
|
||||
"indexIcon": "calendar-outline",
|
||||
"menuColor": "#6644FF",
|
||||
"menuIcon": "calendar-solid",
|
||||
"menuText": "Add new Event",
|
||||
"markerIcon" : "marker-calendar",
|
||||
"markerShape" : "square",
|
||||
"markerDefaultColor2": null,
|
||||
"onlyOnePerOwner": false,
|
||||
"index_plus_button": true,
|
||||
"public_edit_items": false,
|
||||
"listed": true,
|
||||
"item_presets": null,
|
||||
"sort": 5
|
||||
},
|
||||
{
|
||||
"_sync_id": "layer-nomads_location",
|
||||
"name": "Nomads Location",
|
||||
"itemType": "type-ON_nomads_location",
|
||||
"userProfileLayer": false,
|
||||
"indexIcon": "users-outline",
|
||||
"menuColor": "#18222F",
|
||||
"menuIcon": "user-solid",
|
||||
"menuText": "Share your Location",
|
||||
"markerIcon" : "marker-user",
|
||||
"markerShape" : "square",
|
||||
"markerDefaultColor2": null,
|
||||
"onlyOnePerOwner": true,
|
||||
"index_plus_button": true,
|
||||
"public_edit_items": false,
|
||||
"listed": true,
|
||||
"item_presets": null,
|
||||
"sort": 1
|
||||
},
|
||||
{
|
||||
"_sync_id": "layer-nomads_base",
|
||||
"name": "Nomads Base",
|
||||
"itemType": "type-ON_nomads_location",
|
||||
"userProfileLayer": false,
|
||||
"indexIcon": "house-outline",
|
||||
"menuColor": "#B05463",
|
||||
"menuIcon" : "house-solid",
|
||||
"menuText": "Share a new Home Base",
|
||||
"markerIcon" : "marker-house",
|
||||
"markerShape" : "square",
|
||||
"markerDefaultColor2": null,
|
||||
"onlyOnePerOwner": true,
|
||||
"index_plus_button": true,
|
||||
"public_edit_items": false,
|
||||
"listed": true,
|
||||
@ -85,42 +26,37 @@
|
||||
"sort": 2
|
||||
},
|
||||
{
|
||||
"_sync_id": "layer-vessels",
|
||||
"name": "Vessels",
|
||||
"itemType": "type-text-gallery",
|
||||
"_sync_id": "layer-events",
|
||||
"name": "Events",
|
||||
"itemType": "type-event",
|
||||
"userProfileLayer": false,
|
||||
"indexIcon": "boat-outline",
|
||||
"menuColor": "#19898F",
|
||||
"menuIcon" : "boat-solid",
|
||||
"menuText": "Add a new Vessel",
|
||||
"markerIcon" : "marker-boat",
|
||||
"menuColor": "#c4037d",
|
||||
"menuText": "Add new Event",
|
||||
"markerIcon" : "marker-calendar",
|
||||
"markerShape" : "square",
|
||||
"markerDefaultColor2": null,
|
||||
"onlyOnePerOwner": true,
|
||||
"index_plus_button": true,
|
||||
"public_edit_items": false,
|
||||
"listed": true,
|
||||
"item_presets": null,
|
||||
"sort": 3
|
||||
},
|
||||
|
||||
{
|
||||
"_sync_id": "layer-basecamps",
|
||||
"name": "Basecamps",
|
||||
"itemType": "type-text-gallery",
|
||||
"userProfileLayer": false,
|
||||
"indexIcon": "camp-solid",
|
||||
"menuColor": "#FFA439",
|
||||
"menuIcon" : "camp-solid",
|
||||
"menuText": "Add a new Basecamp",
|
||||
"markerIcon" : "marker-camp",
|
||||
"_sync_id": "layer-people",
|
||||
"name": "People",
|
||||
"itemType": "type-user-text-gallery",
|
||||
"userProfileLayer": true,
|
||||
"menuColor": "#e87520",
|
||||
"menuText": "Add a new Vessel",
|
||||
"markerIcon" : "marker-user",
|
||||
"markerShape" : "square",
|
||||
"markerDefaultColor2": null,
|
||||
"onlyOnePerOwner": true,
|
||||
"index_plus_button": true,
|
||||
"public_edit_items": false,
|
||||
"listed": true,
|
||||
"item_presets": null,
|
||||
"sort": 4
|
||||
"sort": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -15,23 +15,13 @@
|
||||
"maps_id": "map-local-development"
|
||||
},
|
||||
{
|
||||
"_sync_id": "layer-nomads-location-map-local-development",
|
||||
"layers_id": "layer-nomads_location",
|
||||
"_sync_id": "layer-people-map-local-development",
|
||||
"layers_id": "layer-people",
|
||||
"maps_id": "map-local-development"
|
||||
},
|
||||
{
|
||||
"_sync_id": "layer-nomads-base-map-local-development",
|
||||
"layers_id": "layer-nomads_base",
|
||||
"maps_id": "map-local-development"
|
||||
},
|
||||
{
|
||||
"_sync_id": "layer-vessel-map-local-development",
|
||||
"layers_id": "layer-vessels",
|
||||
"maps_id": "map-local-development"
|
||||
},
|
||||
{
|
||||
"_sync_id": "layer-basecamps-map-local-development",
|
||||
"layers_id": "layer-basecamps",
|
||||
"_sync_id": "layer-places-map-local-development",
|
||||
"layers_id": "layer-places",
|
||||
"maps_id": "map-local-development"
|
||||
}
|
||||
]
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
"_sync_id": "map-local-development",
|
||||
"name": "Local Development",
|
||||
"url": "http://local.development",
|
||||
"logo": "vessel-svg",
|
||||
"logo": "utopia-logo",
|
||||
"zoom": 6,
|
||||
"own_tag_space": true,
|
||||
"center": {
|
||||
|
||||
@ -133,7 +133,9 @@
|
||||
"_sync_id": "marker-point",
|
||||
"id": "point",
|
||||
"size": "12.00000",
|
||||
"image": "point-solid"
|
||||
"image": "point-solid",
|
||||
"image_outline": "map-pin-outline",
|
||||
"size_outline": "18.00000"
|
||||
},
|
||||
{
|
||||
"_sync_id": "marker-puzzle",
|
||||
|
||||
@ -24,6 +24,14 @@
|
||||
"hideWhenEmpty": true,
|
||||
"showMarkdownHint": true,
|
||||
"size": "full"
|
||||
},
|
||||
{
|
||||
"_sync_id": "c960bbfc-5d98-4f6d-ae44-7a2b63d3359b",
|
||||
"dataField": "text",
|
||||
"heading": null,
|
||||
"hideWhenEmpty": true,
|
||||
"showMarkdownHint": true,
|
||||
"size": "full"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -29,6 +29,7 @@
|
||||
"user_updated": null,
|
||||
"date_updated": null,
|
||||
"template": "flex",
|
||||
"show_start_end_input": true,
|
||||
"show_text": true,
|
||||
"show_profile_button" : true,
|
||||
"show_start_end" : true
|
||||
@ -65,6 +66,22 @@
|
||||
"show_name_input" : true,
|
||||
"show_header_view_in_form" : false,
|
||||
"small_form_edit" : false
|
||||
},
|
||||
{
|
||||
"_sync_id": "type-user-text-gallery",
|
||||
"name": "user:text+gallery",
|
||||
"user_created": null,
|
||||
"date_created": "2025-01-01T00:00:00.000Z",
|
||||
"user_updated": null,
|
||||
"date_updated": null,
|
||||
"template": "flex",
|
||||
"show_text": true,
|
||||
"show_profile_button" : true,
|
||||
"show_start_end" : false,
|
||||
"show_text_input" : false,
|
||||
"show_name_input" : false,
|
||||
"show_header_view_in_form" : false,
|
||||
"small_form_edit" : false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
{
|
||||
"collection": "oceannomads_events",
|
||||
"meta": {
|
||||
"accountability": "all",
|
||||
"archive_app_filter": true,
|
||||
"archive_field": null,
|
||||
"archive_value": null,
|
||||
"collapse": "open",
|
||||
"collection": "oceannomads_events",
|
||||
"color": null,
|
||||
"display_template": null,
|
||||
"group": null,
|
||||
"hidden": false,
|
||||
"icon": null,
|
||||
"item_duplication_fields": null,
|
||||
"note": null,
|
||||
"preview_url": null,
|
||||
"singleton": false,
|
||||
"sort": 23,
|
||||
"sort_field": null,
|
||||
"translations": null,
|
||||
"unarchive_value": null,
|
||||
"versioning": false
|
||||
},
|
||||
"schema": {
|
||||
"name": "oceannomads_events"
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
{
|
||||
"collection": "oceannomads_profiles",
|
||||
"meta": {
|
||||
"accountability": "all",
|
||||
"archive_app_filter": true,
|
||||
"archive_field": null,
|
||||
"archive_value": null,
|
||||
"collapse": "open",
|
||||
"collection": "oceannomads_profiles",
|
||||
"color": null,
|
||||
"display_template": null,
|
||||
"group": null,
|
||||
"hidden": false,
|
||||
"icon": null,
|
||||
"item_duplication_fields": null,
|
||||
"note": null,
|
||||
"preview_url": null,
|
||||
"singleton": false,
|
||||
"sort": 24,
|
||||
"sort_field": null,
|
||||
"translations": null,
|
||||
"unarchive_value": null,
|
||||
"versioning": false
|
||||
},
|
||||
"schema": {
|
||||
"name": "oceannomads_profiles"
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,13 @@
|
||||
{
|
||||
"collection": "oceannomads_profiles",
|
||||
"field": "email",
|
||||
"collection": "layers",
|
||||
"field": "item_default_name",
|
||||
"type": "string",
|
||||
"meta": {
|
||||
"collection": "oceannomads_profiles",
|
||||
"collection": "layers",
|
||||
"conditions": null,
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "email",
|
||||
"field": "item_default_name",
|
||||
"group": null,
|
||||
"hidden": false,
|
||||
"interface": "input",
|
||||
@ -15,7 +15,7 @@
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 2,
|
||||
"sort": 16,
|
||||
"special": null,
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
@ -23,10 +23,10 @@
|
||||
"width": "half"
|
||||
},
|
||||
"schema": {
|
||||
"name": "email",
|
||||
"table": "oceannomads_profiles",
|
||||
"name": "item_default_name",
|
||||
"table": "layers",
|
||||
"data_type": "character varying",
|
||||
"default_value": null,
|
||||
"default_value": "item",
|
||||
"max_length": 255,
|
||||
"numeric_precision": null,
|
||||
"numeric_scale": null,
|
||||
@ -18,7 +18,7 @@
|
||||
},
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 16,
|
||||
"sort": 17,
|
||||
"special": [
|
||||
"cast-json"
|
||||
],
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
"options": {},
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 17,
|
||||
"sort": 18,
|
||||
"special": [
|
||||
"m2m"
|
||||
],
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 12,
|
||||
"sort": 11,
|
||||
"special": [
|
||||
"alias",
|
||||
"no-data",
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 15,
|
||||
"sort": 14,
|
||||
"special": [
|
||||
"alias",
|
||||
"no-data",
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 13,
|
||||
"sort": 12,
|
||||
"special": null,
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 10,
|
||||
"sort": 9,
|
||||
"special": [
|
||||
"m2o"
|
||||
],
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 11,
|
||||
"sort": 10,
|
||||
"special": [
|
||||
"cast-boolean"
|
||||
],
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
},
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 14,
|
||||
"sort": 13,
|
||||
"special": [
|
||||
"cast-json"
|
||||
],
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 17,
|
||||
"sort": 16,
|
||||
"special": [
|
||||
"cast-boolean"
|
||||
],
|
||||
|
||||
@ -0,0 +1,46 @@
|
||||
{
|
||||
"collection": "maps",
|
||||
"field": "maplibre",
|
||||
"type": "alias",
|
||||
"meta": {
|
||||
"collection": "maps",
|
||||
"conditions": [
|
||||
{
|
||||
"hidden": false,
|
||||
"name": "Show when maplibre tiles selected",
|
||||
"options": null,
|
||||
"rule": {
|
||||
"_and": [
|
||||
{
|
||||
"tiles_type": {
|
||||
"_eq": "maplibre"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "maplibre",
|
||||
"group": "tile_server",
|
||||
"hidden": true,
|
||||
"interface": "group-detail",
|
||||
"note": "Configuration for MapLibre GL vector tiles",
|
||||
"options": {
|
||||
"start": "open"
|
||||
},
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 3,
|
||||
"special": [
|
||||
"alias",
|
||||
"no-data",
|
||||
"group"
|
||||
],
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
"validation_message": null,
|
||||
"width": "full"
|
||||
}
|
||||
}
|
||||
@ -1,21 +1,23 @@
|
||||
{
|
||||
"collection": "oceannomads_profiles",
|
||||
"field": "avatar_url",
|
||||
"collection": "maps",
|
||||
"field": "maplibre_style",
|
||||
"type": "string",
|
||||
"meta": {
|
||||
"collection": "oceannomads_profiles",
|
||||
"collection": "maps",
|
||||
"conditions": null,
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "avatar_url",
|
||||
"group": null,
|
||||
"field": "maplibre_style",
|
||||
"group": "maplibre",
|
||||
"hidden": false,
|
||||
"interface": "input",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"note": "MapLibre style URL (default: OpenFreeMap Liberty style)",
|
||||
"options": {
|
||||
"placeholder": "https://tiles.openfreemap.org/styles/liberty"
|
||||
},
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 8,
|
||||
"sort": 1,
|
||||
"special": null,
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
@ -23,8 +25,8 @@
|
||||
"width": "full"
|
||||
},
|
||||
"schema": {
|
||||
"name": "avatar_url",
|
||||
"table": "oceannomads_profiles",
|
||||
"name": "maplibre_style",
|
||||
"table": "maps",
|
||||
"data_type": "character varying",
|
||||
"default_value": null,
|
||||
"max_length": 255,
|
||||
@ -0,0 +1,46 @@
|
||||
{
|
||||
"collection": "maps",
|
||||
"field": "raster_tiles",
|
||||
"type": "alias",
|
||||
"meta": {
|
||||
"collection": "maps",
|
||||
"conditions": [
|
||||
{
|
||||
"hidden": false,
|
||||
"name": "Show when raster tiles selected",
|
||||
"options": null,
|
||||
"rule": {
|
||||
"_and": [
|
||||
{
|
||||
"tiles_type": {
|
||||
"_eq": "raster"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "raster_tiles",
|
||||
"group": "tile_server",
|
||||
"hidden": true,
|
||||
"interface": "group-detail",
|
||||
"note": "Configuration for raster tile layers",
|
||||
"options": {
|
||||
"start": "open"
|
||||
},
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 2,
|
||||
"special": [
|
||||
"alias",
|
||||
"no-data",
|
||||
"group"
|
||||
],
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
"validation_message": null,
|
||||
"width": "full"
|
||||
}
|
||||
}
|
||||
@ -1,32 +1,34 @@
|
||||
{
|
||||
"collection": "oceannomads_events",
|
||||
"field": "end",
|
||||
"type": "dateTime",
|
||||
"collection": "maps",
|
||||
"field": "show_fullscreen_control",
|
||||
"type": "boolean",
|
||||
"meta": {
|
||||
"collection": "oceannomads_events",
|
||||
"collection": "maps",
|
||||
"conditions": null,
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "end",
|
||||
"group": null,
|
||||
"field": "show_fullscreen_control",
|
||||
"group": "Controls",
|
||||
"hidden": false,
|
||||
"interface": "datetime",
|
||||
"interface": "boolean",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 6,
|
||||
"special": null,
|
||||
"special": [
|
||||
"cast-boolean"
|
||||
],
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
"validation_message": null,
|
||||
"width": "full"
|
||||
"width": "half"
|
||||
},
|
||||
"schema": {
|
||||
"name": "end",
|
||||
"table": "oceannomads_events",
|
||||
"data_type": "timestamp without time zone",
|
||||
"default_value": null,
|
||||
"name": "show_fullscreen_control",
|
||||
"table": "maps",
|
||||
"data_type": "boolean",
|
||||
"default_value": false,
|
||||
"max_length": null,
|
||||
"numeric_precision": null,
|
||||
"numeric_scale": null,
|
||||
@ -15,7 +15,7 @@
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 18,
|
||||
"sort": 17,
|
||||
"special": [
|
||||
"cast-boolean"
|
||||
],
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
},
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 16,
|
||||
"sort": 15,
|
||||
"special": [
|
||||
"alias",
|
||||
"no-data",
|
||||
|
||||
@ -11,11 +11,13 @@
|
||||
"group": "tile_server",
|
||||
"hidden": false,
|
||||
"interface": "input",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"note": "Attribution text for raster tiles",
|
||||
"options": {
|
||||
"placeholder": "© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a>"
|
||||
},
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 2,
|
||||
"sort": 4,
|
||||
"special": null,
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
|
||||
@ -8,11 +8,13 @@
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "tile_server_url",
|
||||
"group": "tile_server",
|
||||
"group": "raster_tiles",
|
||||
"hidden": false,
|
||||
"interface": "input",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"note": "Raster tile server URL template (e.g., https://tile.osmand.net/hd/{z}/{x}/{y}.png)",
|
||||
"options": {
|
||||
"placeholder": "https://tile.osmand.net/hd/{z}/{x}/{y}.png"
|
||||
},
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 1,
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
{
|
||||
"collection": "oceannomads_profiles",
|
||||
"field": "last_name",
|
||||
"type": "string",
|
||||
"collection": "maps",
|
||||
"field": "tile_size",
|
||||
"type": "integer",
|
||||
"meta": {
|
||||
"collection": "oceannomads_profiles",
|
||||
"collection": "maps",
|
||||
"conditions": null,
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "last_name",
|
||||
"group": null,
|
||||
"field": "tile_size",
|
||||
"group": "raster_tiles",
|
||||
"hidden": false,
|
||||
"interface": "input",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 6,
|
||||
"sort": 2,
|
||||
"special": null,
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
@ -23,13 +23,13 @@
|
||||
"width": "half"
|
||||
},
|
||||
"schema": {
|
||||
"name": "last_name",
|
||||
"table": "oceannomads_profiles",
|
||||
"data_type": "character varying",
|
||||
"default_value": null,
|
||||
"max_length": 255,
|
||||
"numeric_precision": null,
|
||||
"numeric_scale": null,
|
||||
"name": "tile_size",
|
||||
"table": "maps",
|
||||
"data_type": "integer",
|
||||
"default_value": 256,
|
||||
"max_length": null,
|
||||
"numeric_precision": 32,
|
||||
"numeric_scale": 0,
|
||||
"is_nullable": true,
|
||||
"is_unique": false,
|
||||
"is_indexed": false,
|
||||
@ -1,21 +1,32 @@
|
||||
{
|
||||
"collection": "oceannomads_events",
|
||||
"field": "creator_email",
|
||||
"collection": "maps",
|
||||
"field": "tiles_type",
|
||||
"type": "string",
|
||||
"meta": {
|
||||
"collection": "oceannomads_events",
|
||||
"collection": "maps",
|
||||
"conditions": null,
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "creator_email",
|
||||
"group": null,
|
||||
"field": "tiles_type",
|
||||
"group": "tile_server",
|
||||
"hidden": false,
|
||||
"interface": "input",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"interface": "select-dropdown",
|
||||
"note": "Choose between raster tiles or vector tiles (MapLibre GL)",
|
||||
"options": {
|
||||
"choices": [
|
||||
{
|
||||
"text": "Raster Tiles",
|
||||
"value": "raster"
|
||||
},
|
||||
{
|
||||
"text": "Vector Tiles (MapLibre GL)",
|
||||
"value": "maplibre"
|
||||
}
|
||||
]
|
||||
},
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 9,
|
||||
"sort": 1,
|
||||
"special": null,
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
@ -23,10 +34,10 @@
|
||||
"width": "full"
|
||||
},
|
||||
"schema": {
|
||||
"name": "creator_email",
|
||||
"table": "oceannomads_events",
|
||||
"name": "tiles_type",
|
||||
"table": "maps",
|
||||
"data_type": "character varying",
|
||||
"default_value": null,
|
||||
"default_value": "raster",
|
||||
"max_length": 255,
|
||||
"numeric_precision": null,
|
||||
"numeric_scale": null,
|
||||
@ -1,35 +1,37 @@
|
||||
{
|
||||
"collection": "oceannomads_events",
|
||||
"field": "text",
|
||||
"type": "text",
|
||||
"collection": "maps",
|
||||
"field": "zoom_offset",
|
||||
"type": "integer",
|
||||
"meta": {
|
||||
"collection": "oceannomads_events",
|
||||
"collection": "maps",
|
||||
"conditions": null,
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "text",
|
||||
"group": null,
|
||||
"field": "zoom_offset",
|
||||
"group": "raster_tiles",
|
||||
"hidden": false,
|
||||
"interface": "input-multiline",
|
||||
"interface": "input",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"options": {
|
||||
"min": 0
|
||||
},
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 7,
|
||||
"sort": 3,
|
||||
"special": null,
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
"validation_message": null,
|
||||
"width": "full"
|
||||
"width": "half"
|
||||
},
|
||||
"schema": {
|
||||
"name": "text",
|
||||
"table": "oceannomads_events",
|
||||
"data_type": "text",
|
||||
"default_value": null,
|
||||
"name": "zoom_offset",
|
||||
"table": "maps",
|
||||
"data_type": "integer",
|
||||
"default_value": -1,
|
||||
"max_length": null,
|
||||
"numeric_precision": null,
|
||||
"numeric_scale": null,
|
||||
"numeric_precision": 32,
|
||||
"numeric_scale": 0,
|
||||
"is_nullable": true,
|
||||
"is_unique": false,
|
||||
"is_indexed": false,
|
||||
@ -1,47 +0,0 @@
|
||||
{
|
||||
"collection": "oceannomads_events",
|
||||
"field": "date_created",
|
||||
"type": "timestamp",
|
||||
"meta": {
|
||||
"collection": "oceannomads_events",
|
||||
"conditions": null,
|
||||
"display": "datetime",
|
||||
"display_options": {
|
||||
"relative": true
|
||||
},
|
||||
"field": "date_created",
|
||||
"group": null,
|
||||
"hidden": true,
|
||||
"interface": "datetime",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"readonly": true,
|
||||
"required": false,
|
||||
"sort": 2,
|
||||
"special": [
|
||||
"date-created"
|
||||
],
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
"validation_message": null,
|
||||
"width": "half"
|
||||
},
|
||||
"schema": {
|
||||
"name": "date_created",
|
||||
"table": "oceannomads_events",
|
||||
"data_type": "timestamp with time zone",
|
||||
"default_value": null,
|
||||
"max_length": null,
|
||||
"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
|
||||
}
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
{
|
||||
"collection": "oceannomads_events",
|
||||
"field": "date_updated",
|
||||
"type": "timestamp",
|
||||
"meta": {
|
||||
"collection": "oceannomads_events",
|
||||
"conditions": null,
|
||||
"display": "datetime",
|
||||
"display_options": {
|
||||
"relative": true
|
||||
},
|
||||
"field": "date_updated",
|
||||
"group": null,
|
||||
"hidden": true,
|
||||
"interface": "datetime",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"readonly": true,
|
||||
"required": false,
|
||||
"sort": 3,
|
||||
"special": [
|
||||
"date-updated"
|
||||
],
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
"validation_message": null,
|
||||
"width": "half"
|
||||
},
|
||||
"schema": {
|
||||
"name": "date_updated",
|
||||
"table": "oceannomads_events",
|
||||
"data_type": "timestamp with time zone",
|
||||
"default_value": null,
|
||||
"max_length": null,
|
||||
"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
|
||||
}
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
{
|
||||
"collection": "oceannomads_events",
|
||||
"field": "id",
|
||||
"type": "string",
|
||||
"meta": {
|
||||
"collection": "oceannomads_events",
|
||||
"conditions": null,
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "id",
|
||||
"group": null,
|
||||
"hidden": false,
|
||||
"interface": "input",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 1,
|
||||
"special": null,
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
"validation_message": null,
|
||||
"width": "full"
|
||||
},
|
||||
"schema": {
|
||||
"name": "id",
|
||||
"table": "oceannomads_events",
|
||||
"data_type": "character varying",
|
||||
"default_value": null,
|
||||
"max_length": 255,
|
||||
"numeric_precision": null,
|
||||
"numeric_scale": null,
|
||||
"is_nullable": false,
|
||||
"is_unique": true,
|
||||
"is_indexed": false,
|
||||
"is_primary_key": true,
|
||||
"is_generated": false,
|
||||
"generation_expression": null,
|
||||
"has_auto_increment": false,
|
||||
"foreign_key_table": null,
|
||||
"foreign_key_column": null
|
||||
}
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
{
|
||||
"collection": "oceannomads_profiles",
|
||||
"field": "date_created",
|
||||
"type": "timestamp",
|
||||
"meta": {
|
||||
"collection": "oceannomads_profiles",
|
||||
"conditions": null,
|
||||
"display": "datetime",
|
||||
"display_options": {
|
||||
"relative": true
|
||||
},
|
||||
"field": "date_created",
|
||||
"group": null,
|
||||
"hidden": true,
|
||||
"interface": "datetime",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"readonly": true,
|
||||
"required": false,
|
||||
"sort": 3,
|
||||
"special": [
|
||||
"date-created"
|
||||
],
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
"validation_message": null,
|
||||
"width": "half"
|
||||
},
|
||||
"schema": {
|
||||
"name": "date_created",
|
||||
"table": "oceannomads_profiles",
|
||||
"data_type": "timestamp with time zone",
|
||||
"default_value": null,
|
||||
"max_length": null,
|
||||
"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
|
||||
}
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
{
|
||||
"collection": "oceannomads_profiles",
|
||||
"field": "date_updated",
|
||||
"type": "timestamp",
|
||||
"meta": {
|
||||
"collection": "oceannomads_profiles",
|
||||
"conditions": null,
|
||||
"display": "datetime",
|
||||
"display_options": {
|
||||
"relative": true
|
||||
},
|
||||
"field": "date_updated",
|
||||
"group": null,
|
||||
"hidden": true,
|
||||
"interface": "datetime",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"readonly": true,
|
||||
"required": false,
|
||||
"sort": 4,
|
||||
"special": [
|
||||
"date-updated"
|
||||
],
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
"validation_message": null,
|
||||
"width": "half"
|
||||
},
|
||||
"schema": {
|
||||
"name": "date_updated",
|
||||
"table": "oceannomads_profiles",
|
||||
"data_type": "timestamp with time zone",
|
||||
"default_value": null,
|
||||
"max_length": null,
|
||||
"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
|
||||
}
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
{
|
||||
"collection": "oceannomads_profiles",
|
||||
"field": "id",
|
||||
"type": "string",
|
||||
"meta": {
|
||||
"collection": "oceannomads_profiles",
|
||||
"conditions": null,
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "id",
|
||||
"group": null,
|
||||
"hidden": false,
|
||||
"interface": "input",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 1,
|
||||
"special": null,
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
"validation_message": null,
|
||||
"width": "half"
|
||||
},
|
||||
"schema": {
|
||||
"name": "id",
|
||||
"table": "oceannomads_profiles",
|
||||
"data_type": "character varying",
|
||||
"default_value": null,
|
||||
"max_length": 255,
|
||||
"numeric_precision": null,
|
||||
"numeric_scale": null,
|
||||
"is_nullable": false,
|
||||
"is_unique": true,
|
||||
"is_indexed": false,
|
||||
"is_primary_key": true,
|
||||
"is_generated": false,
|
||||
"generation_expression": null,
|
||||
"has_auto_increment": false,
|
||||
"foreign_key_table": null,
|
||||
"foreign_key_column": null
|
||||
}
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
{
|
||||
"collection": "oceannomads_profiles",
|
||||
"field": "location",
|
||||
"type": "string",
|
||||
"meta": {
|
||||
"collection": "oceannomads_profiles",
|
||||
"conditions": null,
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "location",
|
||||
"group": null,
|
||||
"hidden": false,
|
||||
"interface": "input",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 7,
|
||||
"special": null,
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
"validation_message": null,
|
||||
"width": "full"
|
||||
},
|
||||
"schema": {
|
||||
"name": "location",
|
||||
"table": "oceannomads_profiles",
|
||||
"data_type": "character varying",
|
||||
"default_value": null,
|
||||
"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
|
||||
}
|
||||
}
|
||||
@ -1,21 +1,34 @@
|
||||
{
|
||||
"collection": "oceannomads_events",
|
||||
"field": "location",
|
||||
"collection": "relations",
|
||||
"field": "direction",
|
||||
"type": "string",
|
||||
"meta": {
|
||||
"collection": "oceannomads_events",
|
||||
"collection": "relations",
|
||||
"conditions": null,
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "location",
|
||||
"field": "direction",
|
||||
"group": null,
|
||||
"hidden": false,
|
||||
"interface": "input",
|
||||
"interface": "select-dropdown",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"options": {
|
||||
"choices": [
|
||||
{
|
||||
"icon": "arrow_forward",
|
||||
"text": "outgoing",
|
||||
"value": "outgoing"
|
||||
},
|
||||
{
|
||||
"icon": "arrow_back",
|
||||
"text": "ingoing",
|
||||
"value": "ingoing"
|
||||
}
|
||||
]
|
||||
},
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 8,
|
||||
"sort": 4,
|
||||
"special": null,
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
@ -23,10 +36,10 @@
|
||||
"width": "full"
|
||||
},
|
||||
"schema": {
|
||||
"name": "location",
|
||||
"table": "oceannomads_events",
|
||||
"name": "direction",
|
||||
"table": "relations",
|
||||
"data_type": "character varying",
|
||||
"default_value": null,
|
||||
"default_value": "outgoing",
|
||||
"max_length": 255,
|
||||
"numeric_precision": null,
|
||||
"numeric_scale": null,
|
||||
@ -1,13 +1,13 @@
|
||||
{
|
||||
"collection": "oceannomads_events",
|
||||
"field": "title",
|
||||
"collection": "relations",
|
||||
"field": "heading",
|
||||
"type": "string",
|
||||
"meta": {
|
||||
"collection": "oceannomads_events",
|
||||
"collection": "relations",
|
||||
"conditions": null,
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "title",
|
||||
"field": "heading",
|
||||
"group": null,
|
||||
"hidden": false,
|
||||
"interface": "input",
|
||||
@ -15,7 +15,7 @@
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 4,
|
||||
"sort": 3,
|
||||
"special": null,
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
@ -23,10 +23,10 @@
|
||||
"width": "full"
|
||||
},
|
||||
"schema": {
|
||||
"name": "title",
|
||||
"table": "oceannomads_events",
|
||||
"name": "heading",
|
||||
"table": "relations",
|
||||
"data_type": "character varying",
|
||||
"default_value": null,
|
||||
"default_value": "Relations",
|
||||
"max_length": 255,
|
||||
"numeric_precision": null,
|
||||
"numeric_scale": null,
|
||||
@ -1,32 +1,34 @@
|
||||
{
|
||||
"collection": "oceannomads_events",
|
||||
"field": "start",
|
||||
"type": "dateTime",
|
||||
"collection": "relations",
|
||||
"field": "hideWhenEmpty",
|
||||
"type": "boolean",
|
||||
"meta": {
|
||||
"collection": "oceannomads_events",
|
||||
"collection": "relations",
|
||||
"conditions": null,
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "start",
|
||||
"field": "hideWhenEmpty",
|
||||
"group": null,
|
||||
"hidden": false,
|
||||
"interface": "datetime",
|
||||
"interface": "boolean",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 5,
|
||||
"special": null,
|
||||
"special": [
|
||||
"cast-boolean"
|
||||
],
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
"validation_message": null,
|
||||
"width": "full"
|
||||
},
|
||||
"schema": {
|
||||
"name": "start",
|
||||
"table": "oceannomads_events",
|
||||
"data_type": "timestamp without time zone",
|
||||
"default_value": null,
|
||||
"name": "hideWhenEmpty",
|
||||
"table": "relations",
|
||||
"data_type": "boolean",
|
||||
"default_value": true,
|
||||
"max_length": null,
|
||||
"numeric_precision": null,
|
||||
"numeric_scale": null,
|
||||
@ -0,0 +1,64 @@
|
||||
{
|
||||
"collection": "types",
|
||||
"field": "Flex",
|
||||
"type": "alias",
|
||||
"meta": {
|
||||
"collection": "types",
|
||||
"conditions": [
|
||||
{
|
||||
"name": "Flex-Template",
|
||||
"options": {
|
||||
"start": "open"
|
||||
},
|
||||
"readonly": false,
|
||||
"required": true,
|
||||
"rule": {
|
||||
"_and": [
|
||||
{
|
||||
"template": {
|
||||
"_eq": "flex"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "Not Flex Template",
|
||||
"options": {
|
||||
"start": "closed"
|
||||
},
|
||||
"readonly": true,
|
||||
"rule": {
|
||||
"_and": [
|
||||
{
|
||||
"template": {
|
||||
"_neq": "flex"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "Flex",
|
||||
"group": "Profile",
|
||||
"hidden": false,
|
||||
"interface": "group-raw",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 2,
|
||||
"special": [
|
||||
"alias",
|
||||
"no-data",
|
||||
"group"
|
||||
],
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
"validation_message": null,
|
||||
"width": "full"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
{
|
||||
"collection": "types",
|
||||
"field": "Header",
|
||||
"type": "alias",
|
||||
"meta": {
|
||||
"collection": "types",
|
||||
"conditions": null,
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "Header",
|
||||
"group": null,
|
||||
"hidden": false,
|
||||
"interface": "group-detail",
|
||||
"note": null,
|
||||
"options": {
|
||||
"headerIcon": "credit_card",
|
||||
"start": "closed"
|
||||
},
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 7,
|
||||
"special": [
|
||||
"alias",
|
||||
"no-data",
|
||||
"group"
|
||||
],
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
"validation_message": null,
|
||||
"width": "full"
|
||||
}
|
||||
}
|
||||
@ -12,10 +12,13 @@
|
||||
"hidden": false,
|
||||
"interface": "group-detail",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"options": {
|
||||
"headerIcon": "lab_profile",
|
||||
"start": "closed"
|
||||
},
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 9,
|
||||
"sort": 10,
|
||||
"special": [
|
||||
"alias",
|
||||
"no-data",
|
||||
|
||||
@ -4,16 +4,51 @@
|
||||
"type": "alias",
|
||||
"meta": {
|
||||
"collection": "types",
|
||||
"conditions": null,
|
||||
"conditions": [
|
||||
{
|
||||
"name": "Tabs Template",
|
||||
"options": {
|
||||
"start": "open"
|
||||
},
|
||||
"readonly": false,
|
||||
"rule": {
|
||||
"_and": [
|
||||
{
|
||||
"template": {
|
||||
"_eq": "tabs"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "Not Tabs Template",
|
||||
"options": {
|
||||
"start": "closed"
|
||||
},
|
||||
"readonly": true,
|
||||
"rule": {
|
||||
"_and": [
|
||||
{
|
||||
"template": {
|
||||
"_neq": "tabs"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "Tabs",
|
||||
"group": "accordion-ykcgp6",
|
||||
"group": "Profile",
|
||||
"hidden": false,
|
||||
"interface": "group-detail",
|
||||
"interface": "group-raw",
|
||||
"note": null,
|
||||
"options": {
|
||||
"headerColor": "#1A5FB4"
|
||||
"headerColor": "#1A5FB4",
|
||||
"start": "closed"
|
||||
},
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
"field": "active_tabs",
|
||||
"group": "Tabs",
|
||||
"hidden": false,
|
||||
"interface": "group-detail",
|
||||
"interface": "group-raw",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
|
||||
@ -1,14 +1,30 @@
|
||||
{
|
||||
"collection": "oceannomads_profiles",
|
||||
"field": "first_name",
|
||||
"collection": "types",
|
||||
"field": "cta_button_label",
|
||||
"type": "string",
|
||||
"meta": {
|
||||
"collection": "oceannomads_profiles",
|
||||
"conditions": null,
|
||||
"collection": "types",
|
||||
"conditions": [
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "show cta button",
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"rule": {
|
||||
"_and": [
|
||||
{
|
||||
"show_cta_button": {
|
||||
"_eq": false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "first_name",
|
||||
"group": null,
|
||||
"field": "cta_button_label",
|
||||
"group": "header_elements",
|
||||
"hidden": false,
|
||||
"interface": "input",
|
||||
"note": null,
|
||||
@ -23,8 +39,8 @@
|
||||
"width": "half"
|
||||
},
|
||||
"schema": {
|
||||
"name": "first_name",
|
||||
"table": "oceannomads_profiles",
|
||||
"name": "cta_button_label",
|
||||
"table": "types",
|
||||
"data_type": "character varying",
|
||||
"default_value": null,
|
||||
"max_length": 255,
|
||||
@ -1,21 +1,21 @@
|
||||
{
|
||||
"collection": "types",
|
||||
"field": "accordion-ykcgp6",
|
||||
"field": "header_elements",
|
||||
"type": "alias",
|
||||
"meta": {
|
||||
"collection": "types",
|
||||
"conditions": null,
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "accordion-ykcgp6",
|
||||
"group": "Profile",
|
||||
"field": "header_elements",
|
||||
"group": "Header",
|
||||
"hidden": false,
|
||||
"interface": "group-accordion",
|
||||
"interface": "group-raw",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 2,
|
||||
"sort": 3,
|
||||
"special": [
|
||||
"alias",
|
||||
"no-data",
|
||||
@ -8,14 +8,14 @@
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "profileTemplate",
|
||||
"group": null,
|
||||
"group": "Flex",
|
||||
"hidden": false,
|
||||
"interface": "list-m2a",
|
||||
"note": null,
|
||||
"options": {},
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 11,
|
||||
"sort": 1,
|
||||
"special": [
|
||||
"m2a"
|
||||
],
|
||||
|
||||
@ -1,31 +1,31 @@
|
||||
{
|
||||
"collection": "types",
|
||||
"field": "onepager",
|
||||
"field": "show_cta_button",
|
||||
"type": "boolean",
|
||||
"meta": {
|
||||
"collection": "types",
|
||||
"conditions": null,
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "onepager",
|
||||
"group": "accordion-ykcgp6",
|
||||
"field": "show_cta_button",
|
||||
"group": "header_elements",
|
||||
"hidden": false,
|
||||
"interface": "boolean",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 2,
|
||||
"sort": 4,
|
||||
"special": [
|
||||
"cast-boolean"
|
||||
],
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
"validation_message": null,
|
||||
"width": "full"
|
||||
"width": "half"
|
||||
},
|
||||
"schema": {
|
||||
"name": "onepager",
|
||||
"name": "show_cta_button",
|
||||
"table": "types",
|
||||
"data_type": "boolean",
|
||||
"default_value": false,
|
||||
@ -0,0 +1,45 @@
|
||||
{
|
||||
"collection": "types",
|
||||
"field": "show_navigation_button",
|
||||
"type": "boolean",
|
||||
"meta": {
|
||||
"collection": "types",
|
||||
"conditions": null,
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "show_navigation_button",
|
||||
"group": "header_elements",
|
||||
"hidden": false,
|
||||
"interface": "boolean",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 2,
|
||||
"special": [
|
||||
"cast-boolean"
|
||||
],
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
"validation_message": null,
|
||||
"width": "half"
|
||||
},
|
||||
"schema": {
|
||||
"name": "show_navigation_button",
|
||||
"table": "types",
|
||||
"data_type": "boolean",
|
||||
"default_value": false,
|
||||
"max_length": null,
|
||||
"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
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,14 @@
|
||||
{
|
||||
"collection": "types",
|
||||
"field": "text_area",
|
||||
"field": "show_qr_button",
|
||||
"type": "boolean",
|
||||
"meta": {
|
||||
"collection": "types",
|
||||
"conditions": null,
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "text_area",
|
||||
"group": "accordion-ykcgp6",
|
||||
"field": "show_qr_button",
|
||||
"group": "header_elements",
|
||||
"hidden": false,
|
||||
"interface": "boolean",
|
||||
"note": null,
|
||||
@ -22,10 +22,10 @@
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
"validation_message": null,
|
||||
"width": "full"
|
||||
"width": "half"
|
||||
},
|
||||
"schema": {
|
||||
"name": "text_area",
|
||||
"name": "show_qr_button",
|
||||
"table": "types",
|
||||
"data_type": "boolean",
|
||||
"default_value": false,
|
||||
@ -0,0 +1,45 @@
|
||||
{
|
||||
"collection": "types",
|
||||
"field": "show_share_button",
|
||||
"type": "boolean",
|
||||
"meta": {
|
||||
"collection": "types",
|
||||
"conditions": null,
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "show_share_button",
|
||||
"group": "header_elements",
|
||||
"hidden": false,
|
||||
"interface": "boolean",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 3,
|
||||
"special": [
|
||||
"cast-boolean"
|
||||
],
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
"validation_message": null,
|
||||
"width": "half"
|
||||
},
|
||||
"schema": {
|
||||
"name": "show_share_button",
|
||||
"table": "types",
|
||||
"data_type": "boolean",
|
||||
"default_value": false,
|
||||
"max_length": null,
|
||||
"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
|
||||
}
|
||||
}
|
||||
@ -12,10 +12,13 @@
|
||||
"hidden": false,
|
||||
"interface": "group-detail",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"options": {
|
||||
"headerIcon": "edit_square",
|
||||
"start": "closed"
|
||||
},
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 8,
|
||||
"sort": 9,
|
||||
"special": [
|
||||
"alias",
|
||||
"no-data",
|
||||
|
||||
@ -12,10 +12,13 @@
|
||||
"hidden": false,
|
||||
"interface": "group-detail",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"options": {
|
||||
"headerIcon": "wysiwyg",
|
||||
"start": "closed"
|
||||
},
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 7,
|
||||
"sort": 8,
|
||||
"special": [
|
||||
"alias",
|
||||
"no-data",
|
||||
|
||||
@ -0,0 +1,74 @@
|
||||
{
|
||||
"collection": "types",
|
||||
"field": "subtitle_label",
|
||||
"type": "string",
|
||||
"meta": {
|
||||
"collection": "types",
|
||||
"conditions": [
|
||||
{
|
||||
"hidden": false,
|
||||
"name": "subtitle=custom",
|
||||
"readonly": false,
|
||||
"required": true,
|
||||
"rule": {
|
||||
"_and": [
|
||||
{
|
||||
"subtitle_mode": {
|
||||
"_eq": "custom"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "subtitle != custom",
|
||||
"readonly": true,
|
||||
"required": false,
|
||||
"rule": {
|
||||
"_and": [
|
||||
{
|
||||
"subtitle_mode": {
|
||||
"_neq": "custom"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "subtitle_label",
|
||||
"group": "Header",
|
||||
"hidden": false,
|
||||
"interface": "input",
|
||||
"note": null,
|
||||
"options": null,
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 2,
|
||||
"special": null,
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
"validation_message": null,
|
||||
"width": "half"
|
||||
},
|
||||
"schema": {
|
||||
"name": "subtitle_label",
|
||||
"table": "types",
|
||||
"data_type": "character varying",
|
||||
"default_value": "Subname",
|
||||
"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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
{
|
||||
"collection": "types",
|
||||
"field": "subtitle_mode",
|
||||
"type": "string",
|
||||
"meta": {
|
||||
"collection": "types",
|
||||
"conditions": null,
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "subtitle_mode",
|
||||
"group": "Header",
|
||||
"hidden": false,
|
||||
"interface": "select-dropdown",
|
||||
"note": null,
|
||||
"options": {
|
||||
"choices": [
|
||||
{
|
||||
"text": "address",
|
||||
"value": "address"
|
||||
},
|
||||
{
|
||||
"text": "custom",
|
||||
"value": "custom"
|
||||
},
|
||||
{
|
||||
"text": "none",
|
||||
"value": "none"
|
||||
}
|
||||
]
|
||||
},
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 1,
|
||||
"special": null,
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
"validation_message": null,
|
||||
"width": "half"
|
||||
},
|
||||
"schema": {
|
||||
"name": "subtitle_mode",
|
||||
"table": "types",
|
||||
"data_type": "character varying",
|
||||
"default_value": "address",
|
||||
"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
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,7 @@
|
||||
"display": null,
|
||||
"display_options": null,
|
||||
"field": "template",
|
||||
"group": null,
|
||||
"group": "Profile",
|
||||
"hidden": false,
|
||||
"interface": "select-dropdown",
|
||||
"note": null,
|
||||
@ -34,7 +34,7 @@
|
||||
},
|
||||
"readonly": false,
|
||||
"required": false,
|
||||
"sort": 10,
|
||||
"sort": 1,
|
||||
"special": null,
|
||||
"translations": null,
|
||||
"validation": null,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": 1,
|
||||
"directus": "11.7.2",
|
||||
"directus": "11.9.3",
|
||||
"vendor": "postgres"
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -64,3 +64,30 @@ ON CONFLICT (id) DO UPDATE
|
||||
item = excluded.item,
|
||||
sort = excluded.sort,
|
||||
types_id = excluded.types_id;
|
||||
|
||||
-- Type: user:text+gallery
|
||||
INSERT INTO public."types_profileTemplate" (collection, id, item, sort, types_id)
|
||||
SELECT
|
||||
'texts', '6', 'c960bbfc-5d98-4f6d-ae44-7a2b63d3359b' , '1', types.id
|
||||
FROM
|
||||
public.types as types
|
||||
WHERE
|
||||
name = 'user:text+gallery'
|
||||
ON CONFLICT (id) DO UPDATE
|
||||
SET collection = excluded.collection,
|
||||
item = excluded.item,
|
||||
sort = excluded.sort,
|
||||
types_id = excluded.types_id;
|
||||
|
||||
INSERT INTO public."types_profileTemplate" (collection, id, item, sort, types_id)
|
||||
SELECT
|
||||
'gallery', '7', '6d18b616-6f4f-4987-9860-681b88bdc068' , '2', types.id
|
||||
FROM
|
||||
public.types as types
|
||||
WHERE
|
||||
name = 'user:text+gallery'
|
||||
ON CONFLICT (id) DO UPDATE
|
||||
SET collection = excluded.collection,
|
||||
item = excluded.item,
|
||||
sort = excluded.sort,
|
||||
types_id = excluded.types_id;
|
||||
@ -1,6 +1,9 @@
|
||||
{
|
||||
"name": "directus-extensions",
|
||||
"engines": {
|
||||
"node": ">=22.20.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"directus-extension-sync": "^3.0.4"
|
||||
"directus-extension-sync": "3.0.4"
|
||||
}
|
||||
}
|
||||
24
backend/pull.sh
Executable file
24
backend/pull.sh
Executable file
@ -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
|
||||
24
backend/push.sh
Executable file
24
backend/push.sh
Executable file
@ -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
|
||||
@ -15,16 +15,8 @@ PGDATABASE="${PGDATABASE:-'directus'}"
|
||||
PROJECT_NAME="${PROJECT:-development}"
|
||||
PROJECT_FOLDER=$SCRIPT_DIR/directus-config/$PROJECT_NAME
|
||||
|
||||
echo "Sync collections"
|
||||
npx directus-sync 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 seed push \
|
||||
npx directus-sync@3.4.0 seed push \
|
||||
--seed-path $PROJECT_FOLDER/seed \
|
||||
--directus-url $DIRECTUS_URL \
|
||||
--directus-email $DIRECTUS_EMAIL \
|
||||
|
||||
169
cypress/README.md
Normal file
169
cypress/README.md
Normal file
@ -0,0 +1,169 @@
|
||||
# Cypress End-to-End Tests
|
||||
|
||||
This directory contains **end-to-end tests** for the **Utopia Map application** using [Cypress](https://www.cypress.io/).
|
||||
|
||||
## Technology Stack
|
||||
|
||||
- **Cypress** - E2E testing framework with TypeScript
|
||||
- **cypress-split** - Parallel test execution for faster runs - in the CI pipeline and locally
|
||||
- **mochawesome** - HTML report generation with embedded screenshots for the Github CI pipeline
|
||||
|
||||
## GitHub CI Integration
|
||||
|
||||
Tests run automatically on every push via the `.github/workflows/test.e2e.yml` workflow:
|
||||
|
||||
1. **Build** - Compiles the library and frontend application
|
||||
2. **Start Services** - Launches Docker stack (frontend, backend, database)
|
||||
3. **Seed Data** - Populates database with test data
|
||||
4. **Run Tests** - Executes tests in parallel using `cypress-split`
|
||||
5. **Generate Reports** - Creates consolidated HTML report with screenshots
|
||||
6. **Upload Artifacts** - On failure, uploads test reports for debugging
|
||||
|
||||
### Parallel Execution
|
||||
|
||||
Tests run in parallel using [cypress-split](https://github.com/bahmutov/cypress-split), automatically distributing spec files across multiple processes to reduce execution time.
|
||||
|
||||
### Test Reports
|
||||
|
||||
When tests fail, GitHub Actions automatically uploads as artefact:
|
||||
- **HTML Report**
|
||||
- Interactive test results with embedded screenshots
|
||||
- Available for 14 days with unique naming: `e2e-test-report-{run-id}`
|
||||
|
||||
## Running Tests Locally
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Node.js (version from `.tool-versions`)
|
||||
- Docker and Docker Compose
|
||||
- Sufficient disk space (~2GB for Docker images)
|
||||
|
||||
### Headless Mode (CI-like)
|
||||
|
||||
Run tests without GUI, replicating the CI environment:
|
||||
|
||||
```bash
|
||||
# 1. Set Node.js version
|
||||
nvm use
|
||||
|
||||
# 2. Build the library and frontend
|
||||
cd lib && npm install && npm run build && cd ..
|
||||
|
||||
# 3.Build the frontend && cd ..
|
||||
cd app && cp .env.dist .env
|
||||
sed -i '/VITE_DIRECTUS_ADMIN_ROLE=/c\VITE_DIRECTUS_ADMIN_ROLE=8141dee8-8e10-48d0-baf1-680aea271298' .env
|
||||
npm ci && npm run build && cd ..
|
||||
|
||||
# 3. Start Docker services
|
||||
docker compose up -d
|
||||
|
||||
# 4. Wait for services and seed data
|
||||
timeout 120 bash -c 'until curl -f http://localhost:8055/server/health; do sleep 5; done'
|
||||
cd backend && ./seed.sh && cd ..
|
||||
|
||||
# 5. Run tests
|
||||
cd cypress && npm ci
|
||||
|
||||
# Run all tests in parallel (like CI)
|
||||
npm run test:split:auto
|
||||
|
||||
# Or run tests sequentially
|
||||
npm test
|
||||
|
||||
# Or run specific test file
|
||||
npx cypress run --e2e --browser chromium --spec "e2e/authentication/login.cy.ts"
|
||||
```
|
||||
|
||||
### GUI Mode (Development)
|
||||
|
||||
Run tests with interactive GUI for debugging:
|
||||
|
||||
```bash
|
||||
# 1-4. Follow steps 1-4 from headless mode above
|
||||
|
||||
# 5. Open Cypress GUI
|
||||
cd cypress && npm ci && npm run test:open
|
||||
```
|
||||
|
||||
#### GUI Features
|
||||
|
||||
- **Live Reload** - Tests auto-reload when you save changes
|
||||
- **Time Travel** - Click commands to see DOM snapshots
|
||||
- **Selector Playground** - Interactive tool to find element selectors
|
||||
- **Network Inspection** - View all XHR/fetch requests
|
||||
- **Debug Tools** - Use browser DevTools for debugging
|
||||
|
||||
### Cleanup
|
||||
|
||||
```bash
|
||||
# Stop containers
|
||||
docker compose down
|
||||
|
||||
# Remove database data (for fresh start)
|
||||
sudo rm -rf ./data/database
|
||||
```
|
||||
|
||||
## Test Reports
|
||||
|
||||
After running tests, reports are generated in `cypress/results/`:
|
||||
|
||||
- **HTML Report** - `results/html/merged-report.html` - Interactive report with charts
|
||||
- **Screenshots** - `results/html/screenshots/` - Failure screenshots embedded in HTML
|
||||
- **JSON Data** - `results/*.json` - Raw test data for custom processing
|
||||
|
||||
Generate reports manually:
|
||||
```bash
|
||||
npm run report:merge # Merge parallel test results
|
||||
npm run report:generate # Create HTML report
|
||||
```
|
||||
|
||||
## Writing Tests
|
||||
|
||||
Tests are located in `cypress/e2e/` and follow this structure:
|
||||
|
||||
```typescript
|
||||
describe('Feature Name', () => {
|
||||
beforeEach(() => {
|
||||
cy.clearCookies()
|
||||
cy.clearLocalStorage()
|
||||
cy.visit('/page-url')
|
||||
})
|
||||
|
||||
it('should perform expected behavior', () => {
|
||||
cy.get('[data-cy="input"]').type('value')
|
||||
cy.get('[data-cy="submit"]').click()
|
||||
cy.url().should('include', '/success')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Best Practices
|
||||
|
||||
- Use `[data-cy="..."]` selectors for stability
|
||||
- Test user behavior, rather than just implementation details
|
||||
- Keep tests isolated and independent
|
||||
- Use custom commands for common patterns
|
||||
- Clear state in `beforeEach` hooks
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Tests fail with "baseUrl not reachable"**
|
||||
```bash
|
||||
cd app && npm run build && cd .. && docker compose up -d
|
||||
```
|
||||
|
||||
**Backend health check fails**
|
||||
```bash
|
||||
docker compose logs backend
|
||||
docker compose down && docker compose up -d
|
||||
```
|
||||
|
||||
**Seeding fails with "ConflictError"**
|
||||
```bash
|
||||
docker compose down && sudo rm -rf ./data/database && docker compose up -d
|
||||
```
|
||||
|
||||
**Permission denied on ./data**
|
||||
```bash
|
||||
sudo chmod 777 -R ./data
|
||||
```
|
||||
67
cypress/cypress.config.ts
Normal file
67
cypress/cypress.config.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import { defineConfig } from 'cypress'
|
||||
const cypressSplit = require('cypress-split')
|
||||
|
||||
export default defineConfig({
|
||||
e2e: {
|
||||
baseUrl: 'http://localhost:8080',
|
||||
viewportWidth: 1280,
|
||||
viewportHeight: 720,
|
||||
|
||||
specPattern: 'e2e/**/*.cy.ts',
|
||||
supportFile: 'support/e2e.ts',
|
||||
screenshotsFolder: 'screenshots',
|
||||
videosFolder: 'videos',
|
||||
video: false,
|
||||
screenshotOnRunFailure: true,
|
||||
|
||||
reporter: 'mochawesome',
|
||||
reporterOptions: {
|
||||
reportDir: 'results',
|
||||
reportFilename: '[name]',
|
||||
overwrite: false,
|
||||
html: false, // Only generate JSON during test runs for merging
|
||||
json: true, // Generate JSON for merging
|
||||
embeddedScreenshots: true,
|
||||
useInlineDiffs: true,
|
||||
screenshotOnRunFailure: true
|
||||
},
|
||||
|
||||
defaultCommandTimeout: 10000,
|
||||
requestTimeout: 10000,
|
||||
responseTimeout: 10000,
|
||||
pageLoadTimeout: 30000,
|
||||
|
||||
testIsolation: true,
|
||||
|
||||
retries: {
|
||||
runMode: 2,
|
||||
openMode: 0
|
||||
},
|
||||
|
||||
env: {
|
||||
apiUrl: 'http://localhost:8055',
|
||||
validEmail: 'admin@it4c.dev',
|
||||
validPassword: 'admin123',
|
||||
invalidEmail: 'invalid@example.com',
|
||||
invalidPassword: 'wrongpassword'
|
||||
},
|
||||
|
||||
setupNodeEvents(on, config) {
|
||||
// Load cypress-split plugin
|
||||
cypressSplit(on, config)
|
||||
|
||||
// Load parallel reporter plugin
|
||||
const parallelReporter = require('./plugins/parallel-reporter')
|
||||
config = parallelReporter(on, config)
|
||||
|
||||
on('task', {
|
||||
log(message) {
|
||||
console.log(message)
|
||||
return null
|
||||
}
|
||||
})
|
||||
|
||||
return config
|
||||
},
|
||||
},
|
||||
})
|
||||
166
cypress/e2e/authentification/login.cy.ts
Normal file
166
cypress/e2e/authentification/login.cy.ts
Normal file
@ -0,0 +1,166 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
describe('Utopia Map Login', () => {
|
||||
const testData = {
|
||||
validUser: {
|
||||
email: Cypress.env('validEmail'),
|
||||
password: Cypress.env('validPassword')
|
||||
},
|
||||
invalidUser: {
|
||||
email: Cypress.env('invalidEmail'),
|
||||
password: Cypress.env('invalidPassword')
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
cy.clearCookies()
|
||||
cy.clearLocalStorage()
|
||||
cy.window().then((win) => {
|
||||
win.sessionStorage.clear()
|
||||
})
|
||||
|
||||
cy.visit('/login')
|
||||
})
|
||||
|
||||
it('should successfully login with valid credentials', () => {
|
||||
cy.intercept('POST', '**/auth/login').as('loginRequest')
|
||||
|
||||
cy.get('input[type="email"]').clear()
|
||||
cy.get('input[type="email"]').type(testData.validUser.email)
|
||||
cy.get('input[type="password"]').clear()
|
||||
cy.get('input[type="password"]').type(testData.validUser.password)
|
||||
cy.get('button:contains("Login")').click()
|
||||
|
||||
cy.wait('@loginRequest').then((interception) => {
|
||||
expect(interception.response?.statusCode).to.eq(200)
|
||||
expect(interception.request.body).to.deep.include({
|
||||
email: testData.validUser.email,
|
||||
password: testData.validUser.password
|
||||
})
|
||||
|
||||
expect(interception.response?.body).to.have.property('data')
|
||||
expect(interception.response?.body.data).to.have.property('access_token')
|
||||
expect(interception.response?.body.data.access_token).to.be.a('string')
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
expect(interception.response?.body.data.access_token).to.not.be.empty
|
||||
})
|
||||
|
||||
cy.get('.Toastify__toast--success', { timeout: 10000 }).should('be.visible')
|
||||
cy.url().should('eq', Cypress.config().baseUrl + '/')
|
||||
})
|
||||
|
||||
it('should show error for missing password', () => {
|
||||
cy.intercept('POST', '**/auth/login').as('loginRequest')
|
||||
|
||||
cy.get('input[type="email"]').type(testData.validUser.email)
|
||||
cy.get('button:contains("Login")').click()
|
||||
|
||||
cy.wait('@loginRequest').then((interception) => {
|
||||
expect(interception.response?.statusCode).to.eq(400)
|
||||
expect(interception.request.body).to.deep.include({
|
||||
email: testData.validUser.email,
|
||||
password: ''
|
||||
})
|
||||
|
||||
expect(interception.response?.body).to.have.property('errors')
|
||||
expect(interception.response?.body.errors).to.be.an('array')
|
||||
expect(interception.response?.body.errors).to.have.length.greaterThan(0)
|
||||
expect(interception.response?.body.errors[0]).to.have.property('message')
|
||||
})
|
||||
|
||||
cy.get('.Toastify__toast--error', { timeout: 10000 }).should('be.visible')
|
||||
cy.url().should('include', '/login')
|
||||
})
|
||||
|
||||
it('should show error for missing email', () => {
|
||||
cy.intercept('POST', '**/auth/login').as('loginRequest')
|
||||
|
||||
cy.get('input[type="password"]').type(testData.validUser.password)
|
||||
cy.get('button:contains("Login")').click()
|
||||
|
||||
cy.wait('@loginRequest').then((interception) => {
|
||||
expect(interception.response?.statusCode).to.eq(400)
|
||||
expect(interception.request.body).to.deep.include({
|
||||
email: '',
|
||||
password: testData.validUser.password
|
||||
})
|
||||
|
||||
expect(interception.response?.body).to.have.property('errors')
|
||||
expect(interception.response?.body.errors).to.be.an('array')
|
||||
expect(interception.response?.body.errors).to.have.length.greaterThan(0)
|
||||
})
|
||||
|
||||
cy.get('.Toastify__toast--error', { timeout: 10000 }).should('be.visible')
|
||||
cy.url().should('include', '/login')
|
||||
})
|
||||
|
||||
it('should show error for missing credentials', () => {
|
||||
cy.intercept('POST', '**/auth/login').as('loginRequest')
|
||||
|
||||
cy.get('button:contains("Login")').click()
|
||||
|
||||
cy.wait('@loginRequest').then((interception) => {
|
||||
expect(interception.response?.statusCode).to.eq(400)
|
||||
expect(interception.request.body).to.deep.include({
|
||||
email: '',
|
||||
password: ''
|
||||
})
|
||||
|
||||
expect(interception.response?.body).to.have.property('errors')
|
||||
expect(interception.response?.body.errors).to.be.an('array')
|
||||
expect(interception.response?.body.errors).to.have.length.greaterThan(0)
|
||||
})
|
||||
|
||||
cy.get('.Toastify__toast--error', { timeout: 10000 }).should('be.visible')
|
||||
cy.url().should('include', '/login')
|
||||
})
|
||||
|
||||
it('should show error for invalid credentials', () => {
|
||||
cy.intercept('POST', '**/auth/login').as('loginRequest')
|
||||
|
||||
cy.get('input[type="email"]').clear()
|
||||
cy.get('input[type="email"]').type(testData.invalidUser.email)
|
||||
cy.get('input[type="password"]').clear()
|
||||
cy.get('input[type="password"]').type(testData.invalidUser.password)
|
||||
cy.get('button:contains("Login")').click()
|
||||
|
||||
cy.wait('@loginRequest').then((interception) => {
|
||||
expect(interception.response?.statusCode).to.eq(401)
|
||||
expect(interception.request.body).to.deep.include({
|
||||
email: testData.invalidUser.email,
|
||||
password: testData.invalidUser.password
|
||||
})
|
||||
|
||||
expect(interception.response?.body).to.have.property('errors')
|
||||
expect(interception.response?.body.errors).to.be.an('array')
|
||||
expect(interception.response?.body.errors[0]).to.have.property('message')
|
||||
expect(interception.response?.body.errors[0].message).to.contain('Invalid user credentials')
|
||||
})
|
||||
|
||||
cy.get('.Toastify__toast--error', { timeout: 10000 }).should('be.visible')
|
||||
cy.url().should('include', '/login')
|
||||
})
|
||||
|
||||
it('should show loading state during login', () => {
|
||||
cy.intercept('POST', '**/auth/login', {
|
||||
delay: 1000,
|
||||
statusCode: 200,
|
||||
body: {
|
||||
data: {
|
||||
access_token: 'test_token_123',
|
||||
expires: 900000,
|
||||
refresh_token: 'refresh_token_123'
|
||||
}
|
||||
}
|
||||
}).as('loginRequest')
|
||||
|
||||
cy.get('input[type="email"]').type(testData.validUser.email)
|
||||
cy.get('input[type="password"]').type(testData.validUser.password)
|
||||
cy.get('button:contains("Login")').click()
|
||||
|
||||
cy.get('.tw\\:loading-spinner', { timeout: 5000 }).should('be.visible')
|
||||
|
||||
cy.wait('@loginRequest')
|
||||
cy.url().should('eq', Cypress.config().baseUrl + '/')
|
||||
})
|
||||
})
|
||||
33
cypress/e2e/authentification/login.form-elements.cy.ts
Normal file
33
cypress/e2e/authentification/login.form-elements.cy.ts
Normal file
@ -0,0 +1,33 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
describe('Utopia Map Login Form Elements', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
cy.clearCookies()
|
||||
cy.clearLocalStorage()
|
||||
cy.window().then((win) => {
|
||||
win.sessionStorage.clear()
|
||||
})
|
||||
|
||||
cy.visit('/login')
|
||||
})
|
||||
|
||||
it('should be displayed correctly', () => {
|
||||
cy.get('h2').should('contain.text', 'Login')
|
||||
cy.get('input[type="email"]')
|
||||
.should('be.visible')
|
||||
.should('have.attr', 'placeholder', 'E-Mail')
|
||||
|
||||
cy.get('input[type="password"]')
|
||||
.should('be.visible')
|
||||
.should('have.attr', 'placeholder', 'Password')
|
||||
|
||||
cy.get('button:contains("Login")')
|
||||
.should('be.visible')
|
||||
.should('not.be.disabled')
|
||||
|
||||
cy.get('a[href="/reset-password"]')
|
||||
.should('be.visible')
|
||||
.should('contain.text', 'Forgot Password?')
|
||||
})
|
||||
})
|
||||
115
cypress/e2e/search/search-flows.cy.ts
Normal file
115
cypress/e2e/search/search-flows.cy.ts
Normal file
@ -0,0 +1,115 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
describe('Utopia Map Search', () => {
|
||||
beforeEach(() => {
|
||||
cy.clearCookies()
|
||||
cy.clearLocalStorage()
|
||||
cy.window().then((win) => {
|
||||
win.sessionStorage.clear()
|
||||
})
|
||||
|
||||
cy.visit('/')
|
||||
cy.waitForMapReady()
|
||||
})
|
||||
|
||||
describe('Item Search', () => {
|
||||
it('should find items by exact name match', () => {
|
||||
cy.searchFor('Tech Meetup Munich')
|
||||
cy.get('[data-cy="search-suggestions"]').should('contain', 'Tech Meetup Munich')
|
||||
})
|
||||
|
||||
it('should find items by partial name match (case insensitive)', () => {
|
||||
cy.searchFor('café collaboration')
|
||||
cy.get('[data-cy="search-suggestions"]').should('contain', 'Café Collaboration London')
|
||||
})
|
||||
|
||||
it('should find items by text content', () => {
|
||||
cy.searchFor('sustainability')
|
||||
cy.get('[data-cy="search-suggestions"]').should('contain', 'Alex Entrepreneur')
|
||||
})
|
||||
|
||||
it('should navigate to item profile when clicking search result', () => {
|
||||
cy.searchFor('welcome')
|
||||
cy.get('[data-cy="search-suggestions"]').within(() => {
|
||||
cy.get('[data-cy="search-item-result"]').contains('Welcome to Utopia Map').click()
|
||||
})
|
||||
|
||||
cy.url().should('match', /\/(item\/|[a-f0-9-]+)/)
|
||||
cy.get('body').should('contain', 'Welcome to Utopia Map')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Geographic Search', () => {
|
||||
it('should find geographic locations and related items for a city', () => {
|
||||
cy.searchFor('Berlin')
|
||||
|
||||
cy.get('[data-cy="search-suggestions"]').within(() => {
|
||||
cy.get('[data-cy="search-geo-result"]').should('contain', 'Berlin')
|
||||
cy.get('[data-cy="search-item-result"]').should('contain', 'Community Garden Berlin')
|
||||
})
|
||||
})
|
||||
|
||||
it('should navigate to geographic location when clicking search result', () => {
|
||||
cy.searchFor('Berlin')
|
||||
|
||||
// Click geographic result -> temporary marker
|
||||
cy.get('[data-cy="search-suggestions"]').within(() => {
|
||||
cy.get('[data-cy="search-geo-result"]').contains('Berlin').click()
|
||||
})
|
||||
|
||||
// User sees temporary marker with location popup
|
||||
cy.get('.leaflet-popup').should('be.visible')
|
||||
cy.get('.leaflet-popup-content').should('contain', 'Berlin')
|
||||
|
||||
// Search input is blurred and suggestions hidden
|
||||
cy.get('[data-cy="search-input"]').should('not.be.focused')
|
||||
cy.get('[data-cy="search-suggestions"]').should('not.exist')
|
||||
})
|
||||
|
||||
it('should find specific addresses and landmarks', () => {
|
||||
cy.searchFor('Wat Arun')
|
||||
cy.get('[data-cy="search-suggestions"]').should('be.visible')
|
||||
|
||||
cy.get('[data-cy="search-suggestions"]').within(() => {
|
||||
cy.contains('Wat Arun').first().click()
|
||||
})
|
||||
|
||||
cy.get('.leaflet-popup').should('be.visible')
|
||||
cy.get('.leaflet-popup-content').should('contain', 'Wat Arun')
|
||||
})
|
||||
|
||||
it('should navigate to precise coordinates', () => {
|
||||
const coordinates = '52.5200,13.4050'
|
||||
cy.searchFor(coordinates)
|
||||
|
||||
// User sees coordinate option with flag icon
|
||||
cy.get('[data-cy="search-suggestions"]').within(() => {
|
||||
cy.get('[data-cy="search-coordinate-result"]').should('contain', coordinates)
|
||||
cy.get('[data-cy="search-coordinate-icon"]').should('exist')
|
||||
cy.get('[data-cy="search-coordinate-result"]').click()
|
||||
})
|
||||
|
||||
cy.get('.leaflet-popup').should('be.visible')
|
||||
cy.get('.leaflet-popup-content').should('contain.text', '52.52, 13.40')
|
||||
})
|
||||
|
||||
it('should differentiate between database items and geographic locations', () => {
|
||||
cy.searchFor('Berlin')
|
||||
|
||||
cy.get('[data-cy="search-suggestions"]').within(() => {
|
||||
// Database item should have custom icon and simple name
|
||||
cy.get('[data-cy="search-item-result"]').first().within(() => {
|
||||
// Should have either an icon or placeholder for database items
|
||||
cy.get('[data-cy="search-item-icon"], [data-cy="search-item-icon-placeholder"]').should('exist')
|
||||
})
|
||||
cy.get('[data-cy="search-item-result"]').should('contain', 'Community Garden Berlin')
|
||||
|
||||
// Geographic result should have magnifying glass icon and detailed info
|
||||
cy.get('[data-cy="search-geo-result"]').first().within(() => {
|
||||
cy.get('[data-cy="search-geo-icon"]').should('exist')
|
||||
cy.get('[data-cy="search-geo-details"]').should('exist')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
133
cypress/eslint.config.mjs
Normal file
133
cypress/eslint.config.mjs
Normal file
@ -0,0 +1,133 @@
|
||||
import js from '@eslint/js'
|
||||
import tseslint from 'typescript-eslint'
|
||||
import pluginCypress from 'eslint-plugin-cypress'
|
||||
|
||||
export default tseslint.config(
|
||||
js.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
files: ['**/*.cy.{js,jsx,ts,tsx}', 'cypress/**/*.{js,jsx,ts,tsx}'],
|
||||
plugins: {
|
||||
cypress: pluginCypress,
|
||||
},
|
||||
languageOptions: {
|
||||
globals: {
|
||||
cy: 'readonly',
|
||||
Cypress: 'readonly',
|
||||
expect: 'readonly',
|
||||
assert: 'readonly',
|
||||
chai: 'readonly',
|
||||
describe: 'readonly',
|
||||
it: 'readonly',
|
||||
beforeEach: 'readonly',
|
||||
afterEach: 'readonly',
|
||||
before: 'readonly',
|
||||
after: 'readonly',
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'cypress/no-assigning-return-values': 'error',
|
||||
'cypress/no-unnecessary-waiting': 'error',
|
||||
'cypress/no-async-tests': 'error',
|
||||
'cypress/unsafe-to-chain-command': 'error',
|
||||
|
||||
'@typescript-eslint/no-unused-vars': ['error', {
|
||||
'argsIgnorePattern': '^_',
|
||||
'varsIgnorePattern': '^_'
|
||||
}],
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
|
||||
'no-console': 'warn',
|
||||
'no-debugger': 'error',
|
||||
'no-unused-vars': 'warn',
|
||||
'prefer-const': 'error',
|
||||
'no-var': 'error',
|
||||
'eqeqeq': ['error', 'always'],
|
||||
'curly': ['error', 'all'],
|
||||
|
||||
'indent': ['error', 2],
|
||||
'quotes': ['error', 'single', { 'avoidEscape': true }],
|
||||
'semi': ['error', 'never'],
|
||||
'comma-dangle': ['error', 'never'],
|
||||
'object-curly-spacing': ['error', 'always'],
|
||||
'array-bracket-spacing': ['error', 'never'],
|
||||
'space-before-function-paren': ['error', 'never'],
|
||||
'keyword-spacing': ['error', { 'before': true, 'after': true }],
|
||||
'space-infix-ops': 'error',
|
||||
'eol-last': ['error', 'always'],
|
||||
'no-trailing-spaces': 'error',
|
||||
'max-len': ['warn', {
|
||||
'code': 100,
|
||||
'ignoreUrls': true,
|
||||
'ignoreStrings': true,
|
||||
'ignoreTemplateLiterals': true
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
files: ['cypress/support/**/*.{js,ts}'],
|
||||
rules: {
|
||||
// Enable console warnings in support files
|
||||
'no-console': 'warn'
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
files: ['cypress.config.{js,ts}', 'eslint.config.js'],
|
||||
rules: {
|
||||
'no-console': 'off',
|
||||
'@typescript-eslint/no-require-imports': 'off'
|
||||
}
|
||||
},
|
||||
|
||||
// Node.js CommonJS files (plugins, etc.) - exclude TypeScript rules
|
||||
{
|
||||
files: ['plugins/**/*.js'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'commonjs',
|
||||
globals: {
|
||||
require: 'readonly',
|
||||
module: 'readonly',
|
||||
exports: 'readonly',
|
||||
process: 'readonly',
|
||||
console: 'readonly',
|
||||
__dirname: 'readonly',
|
||||
__filename: 'readonly',
|
||||
Buffer: 'readonly',
|
||||
global: 'readonly'
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
// Disable TypeScript-specific rules for CommonJS files
|
||||
'@typescript-eslint/no-require-imports': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
|
||||
// Allow CommonJS patterns
|
||||
'no-undef': 'off',
|
||||
'no-console': 'off',
|
||||
|
||||
// Keep basic JS rules
|
||||
'no-unused-vars': 'warn',
|
||||
'prefer-const': 'error',
|
||||
'no-var': 'error'
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
ignores: [
|
||||
'node_modules/**',
|
||||
'cypress/downloads/**',
|
||||
'cypress/screenshots/**',
|
||||
'cypress/videos/**',
|
||||
'cypress/plugins/**', // Ignore Node.js CommonJS plugin files
|
||||
'results/**',
|
||||
'dist/**',
|
||||
'build/**',
|
||||
'*.min.js'
|
||||
]
|
||||
}
|
||||
)
|
||||
6457
cypress/package-lock.json
generated
Normal file
6457
cypress/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
37
cypress/package.json
Normal file
37
cypress/package.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "utopia-map-e2e-testing",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"description": "Cypress End-to-End Tests for Utopia Map",
|
||||
"scripts": {
|
||||
"test": "cypress run --e2e --browser chromium",
|
||||
"test:open": "cypress open --e2e",
|
||||
"report:merge": "mochawesome-merge 'results/*.json' -o results/merged-report.json",
|
||||
"report:generate": "marge results/merged-report.json --reportDir results/html --reportTitle 'Utopia Map E2E Tests' --reportPageTitle 'Utopia Map E2E Test Report' --inline --charts --showPassed --showFailed --showPending --showSkipped",
|
||||
"report:full": "npm run report:merge && npm run report:generate",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix"
|
||||
},
|
||||
"keywords": [
|
||||
"cypress",
|
||||
"cypress-split",
|
||||
"e2e",
|
||||
"mochawesome",
|
||||
"testing",
|
||||
"utopia-map"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.36.0",
|
||||
"@types/mochawesome": "^6.2.4",
|
||||
"@types/node": "^24.5.2",
|
||||
"cypress": "^15.3.0",
|
||||
"cypress-split": "^1.24.23",
|
||||
"eslint": "^9.36.0",
|
||||
"eslint-plugin-cypress": "^5.1.1",
|
||||
"mochawesome": "^7.1.4",
|
||||
"mochawesome-merge": "^4.3.0",
|
||||
"mochawesome-report-generator": "^6.2.0",
|
||||
"typescript": "^5.9.2",
|
||||
"typescript-eslint": "^8.44.1"
|
||||
}
|
||||
}
|
||||
71
cypress/plugins/parallel-reporter.js
Normal file
71
cypress/plugins/parallel-reporter.js
Normal file
@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Cypress Plugin for Enhanced Parallel Test Reporting
|
||||
* Handles mochawesome report generation for parallel test execution
|
||||
*/
|
||||
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
|
||||
module.exports = (on, config) => {
|
||||
// Ensure results directory exists
|
||||
const resultsDir = path.join(config.projectRoot, 'results')
|
||||
if (!fs.existsSync(resultsDir)) {
|
||||
fs.mkdirSync(resultsDir, { recursive: true })
|
||||
}
|
||||
|
||||
// Configure mochawesome for parallel execution - JSON only for merging
|
||||
const splitIndex = process.env.SPLIT_INDEX || '0'
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
|
||||
|
||||
config.reporterOptions = {
|
||||
...config.reporterOptions,
|
||||
reportFilename: `split-${splitIndex}-${timestamp}-[name]`,
|
||||
reportDir: resultsDir,
|
||||
overwrite: false,
|
||||
html: false, // No individual HTML files
|
||||
json: true, // Only JSON for merging into one report
|
||||
embeddedScreenshots: true,
|
||||
useInlineDiffs: true
|
||||
}
|
||||
|
||||
// Task for logging parallel execution info
|
||||
on('task', {
|
||||
log(message) {
|
||||
console.log(`[Parallel ${splitIndex}] ${message}`)
|
||||
return null
|
||||
},
|
||||
|
||||
logReportInfo() {
|
||||
console.log(`[Parallel ${splitIndex}] Report will be saved as: report-${splitIndex}-${timestamp}.json`)
|
||||
return null
|
||||
}
|
||||
})
|
||||
|
||||
// Before run hook
|
||||
on('before:run', (details) => {
|
||||
console.log(`[Parallel ${splitIndex}] Starting test execution`)
|
||||
console.log(`[Parallel ${splitIndex}] Browser: ${details.browser.name}`)
|
||||
console.log(`[Parallel ${splitIndex}] Specs: ${details.specs.length}`)
|
||||
return details
|
||||
})
|
||||
|
||||
// After run hook
|
||||
on('after:run', (results) => {
|
||||
console.log(`[Parallel ${splitIndex}] Test execution completed`)
|
||||
console.log(`[Parallel ${splitIndex}] Total tests: ${results.totalTests}`)
|
||||
console.log(`[Parallel ${splitIndex}] Passed: ${results.totalPassed}`)
|
||||
console.log(`[Parallel ${splitIndex}] Failed: ${results.totalFailed}`)
|
||||
|
||||
// Ensure the report file was created
|
||||
const reportFile = path.join(resultsDir, `report-${splitIndex}-${timestamp}.json`)
|
||||
if (fs.existsSync(reportFile)) {
|
||||
console.log(`[Parallel ${splitIndex}] ✅ Report saved: ${reportFile}`)
|
||||
} else {
|
||||
console.log(`[Parallel ${splitIndex}] ❌ Report not found: ${reportFile}`)
|
||||
}
|
||||
|
||||
return results
|
||||
})
|
||||
|
||||
return config
|
||||
}
|
||||
71
cypress/scripts/create-index-page.sh
Executable file
71
cypress/scripts/create-index-page.sh
Executable file
@ -0,0 +1,71 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "=== Creating index page for consolidated report ==="
|
||||
|
||||
cat > results/html/index.html << 'EOF'
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Utopia Map E2E Test Report</title>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 40px; background: #f8f9fa; }
|
||||
.header { background: white; padding: 20px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 2px 4px #00000020; }
|
||||
.report-section { background: white; padding: 20px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 2px 4px #00000020; }
|
||||
.report-link { display: block; padding: 16px 20px; margin: 12px 0; background: #e3f2fd; border-radius: 8px; text-decoration: none; color: #1976d2; border-left: 4px solid #2196f3; font-size: 18px; font-weight: 500; }
|
||||
.report-link:hover { background: #bbdefb; }
|
||||
.meta { color: #666; font-size: 14px; margin: 4px 0; }
|
||||
.status { padding: 4px 8px; border-radius: 4px; font-size: 12px; font-weight: bold; }
|
||||
.status.failed { background: #ffebee; color: #c62828; }
|
||||
.status.passed { background: #e8f5e8; color: #2e7d32; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Utopia Map E2E Test Report</h1>
|
||||
<div class="meta">Generated: $(date)</div>
|
||||
<div class="meta">Run ID: ${GITHUB_RUN_ID:-unknown}</div>
|
||||
<div class="meta">Commit: ${GITHUB_SHA:-unknown}</div>
|
||||
<div class="meta">Status: <span class="status failed">Tests Failed</span></div>
|
||||
</div>
|
||||
|
||||
<div class="report-section">
|
||||
<h2>📊 Consolidated Test Report</h2>
|
||||
<p>This report contains all test results from the parallel test execution, merged into one comprehensive view with screenshots embedded directly in failing test cases.</p>
|
||||
<a href="merged-report.html" class="report-link">
|
||||
📈 View Complete Test Report (All Specs)
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="report-section">
|
||||
<h2>📸 Test Screenshots</h2>
|
||||
<p>Screenshots are automatically embedded within their respective failing test cases in the main report. No separate screenshot viewing required.</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
setTimeout(() => {
|
||||
window.location.href = 'merged-report.html';
|
||||
}, 3000);
|
||||
|
||||
let countdown = 3;
|
||||
const countdownEl = document.createElement('div');
|
||||
countdownEl.style.cssText = 'position: fixed; top: 20px; right: 20px; background: #2196f3; color: white; padding: 10px 15px; border-radius: 4px; font-size: 14px;';
|
||||
countdownEl.textContent = `Auto-redirecting in ${countdown}s...`;
|
||||
document.body.appendChild(countdownEl);
|
||||
|
||||
const timer = setInterval(() => {
|
||||
countdown--;
|
||||
if (countdown > 0) {
|
||||
countdownEl.textContent = `Auto-redirecting in ${countdown}s...`;
|
||||
} else {
|
||||
clearInterval(timer);
|
||||
countdownEl.textContent = 'Redirecting...';
|
||||
}
|
||||
}, 1000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
|
||||
echo "✅ Simple index page created with auto-redirect to consolidated report"
|
||||
96
cypress/scripts/generate-html-report.sh
Executable file
96
cypress/scripts/generate-html-report.sh
Executable file
@ -0,0 +1,96 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "=== Generating HTML report with embedded screenshots ==="
|
||||
|
||||
if [ ! -f "results/merged-report.json" ]; then
|
||||
echo "❌ No merged JSON found, cannot generate consolidated report"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Generating comprehensive HTML report from merged JSON..."
|
||||
npm run report:generate
|
||||
|
||||
if [ ! -f "results/html/merged-report.html" ]; then
|
||||
echo "❌ HTML generation failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Consolidated HTML report generated successfully"
|
||||
echo "Report size: $(wc -c < results/html/merged-report.html) bytes"
|
||||
|
||||
# Copy screenshots with proper structure for the HTML report
|
||||
echo "Copying screenshots to HTML report directory..."
|
||||
|
||||
if [ -d "screenshots" ]; then
|
||||
echo "Screenshots directory found"
|
||||
|
||||
# Remove unwanted screenshots (like before-intentional-failure)
|
||||
echo "Cleaning up unwanted screenshots..."
|
||||
find screenshots -name "*before-intentional-failure*" -type f -delete 2>/dev/null || true
|
||||
find screenshots -name "*before-*" -type f -delete 2>/dev/null || true
|
||||
|
||||
# Create screenshots directory in the HTML output
|
||||
mkdir -p "results/html/screenshots"
|
||||
|
||||
# Extract all screenshot paths expected by the HTML report and copy them accordingly
|
||||
echo "Extracting screenshot paths from HTML report..."
|
||||
|
||||
# Create screenshots directory in the HTML output
|
||||
mkdir -p "results/html/screenshots"
|
||||
|
||||
if [ -f "results/merged-report.json" ]; then
|
||||
echo "Reading expected screenshot paths from JSON report..."
|
||||
|
||||
# Extract all screenshot paths referenced in the JSON report (from context fields)
|
||||
grep -o 'screenshots/[^"]*\.png' results/merged-report.json | sort -u | while read expected_path; do
|
||||
# Extract components from expected path: screenshots/parent-dir/test-file/filename.png
|
||||
if [[ "$expected_path" =~ screenshots/([^/]+)/([^/]+)/(.+) ]]; then
|
||||
parent_dir="${BASH_REMATCH[1]}"
|
||||
test_file="${BASH_REMATCH[2]}"
|
||||
filename="${BASH_REMATCH[3]}"
|
||||
|
||||
# Try to find the actual screenshot in various possible locations
|
||||
actual_screenshot=""
|
||||
|
||||
# 1. Try full structure first: screenshots/parent-dir/test-file/filename.png
|
||||
if [ -f "screenshots/$parent_dir/$test_file/$filename" ]; then
|
||||
actual_screenshot="screenshots/$parent_dir/$test_file/$filename"
|
||||
# 2. Try flat structure: screenshots/test-file/filename.png
|
||||
elif [ -f "screenshots/$test_file/$filename" ]; then
|
||||
actual_screenshot="screenshots/$test_file/$filename"
|
||||
# 3. Try direct file: screenshots/filename.png
|
||||
elif [ -f "screenshots/$filename" ]; then
|
||||
actual_screenshot="screenshots/$filename"
|
||||
fi
|
||||
|
||||
if [ -n "$actual_screenshot" ] && [ -f "$actual_screenshot" ]; then
|
||||
# Create the expected directory structure in results/html
|
||||
target_path="results/html/$expected_path"
|
||||
target_dir=$(dirname "$target_path")
|
||||
mkdir -p "$target_dir"
|
||||
|
||||
# Copy the screenshot to the expected location
|
||||
cp "$actual_screenshot" "$target_path"
|
||||
echo "Mapped screenshot: $(basename "$test_file") -> $parent_dir/$test_file"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "❌ No JSON report found, cannot determine expected screenshot paths"
|
||||
# Fallback: copy whatever structure exists
|
||||
if [ -d "screenshots" ] && [ "$(find screenshots -name '*.png' | wc -l)" -gt 0 ]; then
|
||||
echo "Fallback: copying existing screenshot structure..."
|
||||
cp -r screenshots/* results/html/screenshots/ 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "✅ Screenshots copied successfully"
|
||||
echo "Final screenshot structure:"
|
||||
find results/html/screenshots -type f -name "*.png" | head -10
|
||||
else
|
||||
echo "⚠️ No screenshots directory found"
|
||||
fi
|
||||
|
||||
echo "=== Final consolidated report ready ==="
|
||||
ls -la results/html/
|
||||
35
cypress/scripts/merge-reports.sh
Executable file
35
cypress/scripts/merge-reports.sh
Executable file
@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "=== Merging all JSON reports into one consolidated report ==="
|
||||
|
||||
json_count=$(find results/ -name "*.json" -type f 2>/dev/null | wc -l)
|
||||
echo "Found $json_count JSON report files from parallel test execution"
|
||||
|
||||
if [ "$json_count" -gt 0 ]; then
|
||||
echo "=== JSON files found ==="
|
||||
find results/ -name "*.json" -type f | sort
|
||||
|
||||
echo "=== Merging all reports into one ==="
|
||||
npm run report:merge
|
||||
|
||||
if [ ! -f "results/merged-report.json" ]; then
|
||||
echo "❌ Merge failed - no merged-report.json created"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Successfully merged $json_count JSON reports into one"
|
||||
echo "Merged report size: $(wc -c < results/merged-report.json) bytes"
|
||||
|
||||
report=$(cat results/merged-report.json)
|
||||
echo "Consolidated report stats:"
|
||||
echo " - Total tests: $(echo "$report" | node -pe 'JSON.parse(require("fs").readFileSync(0)).stats?.tests || 0')"
|
||||
echo " - Passed: $(echo "$report" | node -pe 'JSON.parse(require("fs").readFileSync(0)).stats?.passes || 0')"
|
||||
echo " - Failed: $(echo "$report" | node -pe 'JSON.parse(require("fs").readFileSync(0)).stats?.failures || 0')"
|
||||
echo " - Duration: $(echo "$report" | node -pe 'JSON.parse(require("fs").readFileSync(0)).stats?.duration || 0')ms"
|
||||
else
|
||||
echo "❌ No JSON reports found to merge"
|
||||
echo "Creating empty report structure..."
|
||||
mkdir -p results
|
||||
echo '{"stats":{"tests":0,"passes":0,"failures":0},"results":[]}' > results/merged-report.json
|
||||
fi
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user