Merge pull request #29 from utopia-os/eslint

refactor(frontend): eslint
This commit is contained in:
Ulf Gebhardt 2025-03-17 22:58:48 +01:00 committed by GitHub
commit 02833c79af
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 3044 additions and 1317 deletions

3
.eslintignore Normal file
View File

@ -0,0 +1,3 @@
node_modules/
dist/
data/

223
.eslintrc.cjs Normal file
View File

@ -0,0 +1,223 @@
// eslint-disable-next-line import/no-commonjs
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'standard',
'eslint:recommended',
'plugin:@eslint-community/eslint-comments/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:import/recommended',
'plugin:import/typescript',
// 'plugin:promise/recommended',
'plugin:security/recommended-legacy',
'plugin:react/recommended',
],
parserOptions: {
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
sourceType: 'module',
},
plugins: [
'@typescript-eslint',
'import',
'promise',
'security',
'no-catch-all',
'react',
'react-hooks',
'react-refresh',
],
// TODO also parse this
ignorePatterns: ['vite.config.ts'],
settings: {
'import/resolver': {
typescript: true,
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
},
react: {
version: '18.2.0',
},
},
rules: {
'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks
'react-hooks/exhaustive-deps': 'warn', // Checks effect dependencies
'react/react-in-jsx-scope': 'off', // Disable requirement for React import
'no-catch-all/no-catch-all': 'error',
'no-console': 'error',
'no-debugger': 'error',
camelcase: 'error',
indent: ['error', 2],
'linebreak-style': ['error', 'unix'],
semi: ['error', 'never'],
// Optional eslint-comments rule
'@eslint-community/eslint-comments/no-unused-disable': 'error',
'@eslint-community/eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }],
// import
'import/export': 'error',
'import/no-deprecated': 'error',
'import/no-empty-named-blocks': 'error',
'import/no-extraneous-dependencies': 'error',
'import/no-mutable-exports': 'error',
'import/no-unused-modules': 'error',
'import/no-named-as-default': 'error',
'import/no-named-as-default-member': 'error',
'import/no-amd': 'error',
'import/no-commonjs': 'error',
'import/no-import-module-exports': 'error',
'import/no-nodejs-modules': 'off',
'import/unambiguous': 'off', // not compatible with scriptless vue files
'import/default': 'error',
'import/named': 'error',
'import/namespace': 'error',
'import/no-absolute-path': 'error',
'import/no-cycle': 'error',
'import/no-dynamic-require': 'error',
'import/no-internal-modules': 'off',
'import/no-relative-packages': 'error',
'import/no-relative-parent-imports': [
'error',
{
ignore: ['#[src,types,root,components,utils,assets]/*'],
},
],
'import/no-self-import': 'error',
'import/no-unresolved': [
'error',
{
ignore: ['react'],
},
],
'import/no-useless-path-segments': 'error',
'import/no-webpack-loader-syntax': 'error',
'import/consistent-type-specifier-style': 'error',
'import/exports-last': 'off',
'import/extensions': [
'error',
'never',
{
json: 'always',
},
],
'import/first': 'error',
'import/group-exports': 'off',
'import/newline-after-import': 'error',
'import/no-anonymous-default-export': 'off', // todo - consider to enable again
'import/no-default-export': 'off', // incompatible with vite & vike
'import/no-duplicates': 'error',
'import/no-named-default': 'error',
'import/no-namespace': 'error',
'import/no-unassigned-import': [
'error',
{
allow: ['**/*.css'],
},
],
'import/order': [
'error',
{
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
'newlines-between': 'always',
alphabetize: {
order: 'asc', // sort in ascending order. Options: ["ignore", "asc", "desc"]
caseInsensitive: true, // ignore case. Options: [true, false]
},
distinctGroup: true,
},
],
'import/prefer-default-export': 'off',
// promise
'promise/catch-or-return': 'error',
'promise/no-return-wrap': 'error',
'promise/param-names': 'error',
'promise/always-return': 'error',
'promise/no-native': 'off',
'promise/no-nesting': 'warn',
'promise/no-promise-in-callback': 'warn',
'promise/no-callback-in-promise': 'warn',
'promise/avoid-new': 'warn',
'promise/no-new-statics': 'error',
'promise/no-return-in-finally': 'warn',
'promise/valid-params': 'warn',
'promise/prefer-await-to-callbacks': 'error',
'promise/no-multiple-resolved': 'error',
},
overrides: [
{
files: ['*.ts', '*.tsx'],
parser: '@typescript-eslint/parser',
parserOptions: {
tsconfigRootDir: __dirname,
project: ['./tsconfig.json', '**/tsconfig.json'],
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
sourceType: 'module',
},
plugins: ['@typescript-eslint'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:@typescript-eslint/strict',
],
rules: {
'@typescript-eslint/consistent-type-imports': 'error',
// allow explicitly defined dangling promises
'@typescript-eslint/no-floating-promises': ['error', { ignoreVoid: true }],
'no-void': ['error', { allowAsStatement: true }],
},
},
{
files: ['!*.json'],
plugins: ['prettier'],
extends: ['plugin:prettier/recommended'],
rules: {
'prettier/prettier': 'error',
},
},
{
files: ['*.json'],
plugins: ['json'],
extends: ['plugin:json/recommended-with-comments'],
},
// {
// files: ['*.{test,spec}.[tj]s'],
// plugins: ['vitest'],
// extends: ['plugin:vitest/all'],
// rules: {
// 'vitest/prefer-lowercase-title': 'off',
// 'vitest/no-hooks': 'off',
// 'vitest/consistent-test-filename': 'off',
// 'vitest/prefer-expect-assertions': [
// 'off',
// {
// onlyFunctionsWithExpectInLoop: true,
// onlyFunctionsWithExpectInCallback: true,
// },
// ],
// 'vitest/prefer-strict-equal': 'off',
// 'vitest/prefer-to-be-falsy': 'off',
// 'vitest/prefer-to-be-truthy': 'off',
// 'vitest/require-hook': [
// 'error',
// {
// allowedFunctionCalls: [
// 'mockClient.setRequestHandler',
// 'setActivePinia',
// 'provideApolloClient',
// ],
// },
// ],
// },
// },
{
files: ['*.yaml', '*.yml'],
parser: 'yaml-eslint-parser',
plugins: ['yml'],
extends: ['plugin:yml/prettier'],
},
],
}

33
.github/workflows/test.lint.yml vendored Normal file
View File

@ -0,0 +1,33 @@
name: test:lint
on: push
jobs:
files-changed:
name: Detect File Changes - lint
runs-on: ubuntu-latest
outputs:
lint: ${{ steps.filter.outputs.lint }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: filter
with:
filters: |
lint:
- '.github/workflows/**/*'
- '**/*'
lint:
if: needs.files-changed.outputs.lint == 'true'
name: Lint
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.0.3
with:
node-version-file: './.tool-versions'
- name: Lint
run: npm install && npm run test:lint:eslint
working-directory: ./

14
.prettierrc.json Normal file
View File

@ -0,0 +1,14 @@
{
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"semi": false,
"singleQuote": true,
"quoteProps": "as-needed",
"jsxSingleQuote": true,
"trailingComma": "all",
"bracketSpacing": true,
"bracketSameLine": false,
"arrowParens": "always",
"endOfLine": "auto"
}

1
.tool-versions Normal file
View File

@ -0,0 +1 @@
nodejs 20.12.1

View File

@ -17,4 +17,4 @@ services:
- SECRET=SECRET
- PUBLIC_URL=http://localhost
- ADMIN_EMAIL=admin@it4c.dev
- ADMIN_PASSWORD=admin123
- ADMIN_PASSWORD=admin123

1373
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@
"scripts": {
"dev": "vite --host",
"build": "tsc && vite build",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"test:lint:eslint": "eslint --ext .ts,.tsx,.js,.jsx,.cjs,.mjs,.json,.yml,.yaml --max-warnings 0 .",
"preview": "vite preview"
},
"dependencies": {
@ -22,16 +22,29 @@
"utopia-ui": "^3.0.76"
},
"devDependencies": {
"@eslint-community/eslint-plugin-eslint-comments": "^4.4.1",
"@types/react": "^18.2.79",
"@types/react-dom": "^18.2.25",
"@typescript-eslint/eslint-plugin": "^5.57.1",
"@typescript-eslint/parser": "^5.57.1",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"@vitejs/plugin-react": "^4.0.0",
"autoprefixer": "^10.4.16",
"daisyui": "^4.12.23",
"eslint": "^8.38.0",
"eslint": "^8.24.0",
"eslint-config-prettier": "^9.1.0",
"eslint-config-standard": "^17.1.0",
"eslint-import-resolver-typescript": "^3.6.3",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-json": "^3.1.0",
"eslint-plugin-n": "^16.6.2",
"eslint-plugin-no-catch-all": "^1.1.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-react": "^7.31.8",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.3.4",
"eslint-plugin-react-refresh": "^0.4.18",
"eslint-plugin-security": "^3.0.1",
"eslint-plugin-yml": "^1.14.0",
"postcss": "^8.4.30",
"tailwindcss": "^3.3.3",
"typescript": "^5.0.2",

View File

@ -1,143 +1,198 @@
import { AppShell, SideBar, Content, AuthProvider, Modal, LoginPage, SignupPage, Quests, RequestPasswordPage, SetNewPasswordPage, UserSettings, OverlayItemsIndexPage, ProfileView, ProfileForm, Permissions, Tags, SelectUser, AttestationForm, MarketView } from 'utopia-ui'
import { getBottomRoutes, routes } from './routes/sidebar'
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable import/order */
/* eslint-disable eqeqeq */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable @typescript-eslint/no-floating-promises */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable new-cap */
/* eslint-disable @typescript-eslint/prefer-optional-chain */
/* eslint-disable @typescript-eslint/restrict-plus-operands */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */
import type { Tag } from 'utopia-ui'
import {
AppShell,
SideBar,
Content,
AuthProvider,
Modal,
LoginPage,
SignupPage,
Quests,
RequestPasswordPage,
SetNewPasswordPage,
UserSettings,
OverlayItemsIndexPage,
ProfileView,
ProfileForm,
Permissions,
Tags,
SelectUser,
AttestationForm,
MarketView,
} from 'utopia-ui'
import { Route, Routes } from 'react-router-dom'
import MapContainer from "./pages/MapContainer"
import './App.css'
import { userApi } from './api/userApi'
import { useEffect, useState } from 'react'
import { assetsApi } from './api/assetsApi'
import { itemsApi } from './api/itemsApi'
import { layersApi } from './api/layersApi'
import { mapApi } from './api/mapApi'
import { permissionsApi } from './api/permissionsApi'
import { userApi } from './api/userApi'
import { ModalContent } from './ModalContent'
import { Landingpage } from './pages/Landingpage'
import { useEffect, useState } from 'react'
import { itemsApi } from './api/itemsApi'
import { permissionsApi } from './api/permissionsApi'
import { Tag } from 'utopia-ui'
import { mapApi } from './api/mapApi'
import { layersApi } from './api/layersApi'
import MapContainer from './pages/MapContainer'
import { getBottomRoutes, routes } from './routes/sidebar'
function App() {
const [permissionsApiInstance, setPermissionsApiInstance] = useState<permissionsApi>()
const [tagsApi, setTagsApi] = useState<itemsApi<Tag>>()
const [mapApiInstance, setMapApiInstance] = useState<mapApi>()
const [layersApiInstance, setLayersApiInstance] = useState<layersApi>()
const [attestationApi, setAttestationApi] = useState<itemsApi<any>>()
const [permissionsApiInstance, setPermissionsApiInstance] = useState<permissionsApi>();
const [tagsApi, setTagsApi] = useState<itemsApi<Tag>>();
const [mapApiInstance, setMapApiInstance] = useState<mapApi>();
const [layersApiInstance, setLayersApiInstance] = useState<layersApi>();
const [attestationApi, setAttestationApi] = useState<itemsApi<any>>();
const [map, setMap] = useState<any>();
const [layers, setLayers] = useState<any>();
const [layerPageRoutes, setLayerPageRoutes] = useState<any>();
const [loading, setLoading] = useState<boolean>(true);
const [map, setMap] = useState<any>()
const [layers, setLayers] = useState<any>()
const [layerPageRoutes, setLayerPageRoutes] = useState<any>()
const [loading, setLoading] = useState<boolean>(true)
useEffect(() => {
setPermissionsApiInstance(new permissionsApi());
setMapApiInstance(new mapApi(window.location.origin));
setAttestationApi(new itemsApi<any>("attestations"));
setPermissionsApiInstance(new permissionsApi())
setMapApiInstance(new mapApi(window.location.origin))
setAttestationApi(new itemsApi<any>('attestations'))
}, [])
useEffect(() => {
mapApiInstance && getMap();
mapApiInstance && getMap()
}, [mapApiInstance])
const getMap = async () => {
const map = await mapApiInstance?.getItems();
map && setMap(map);
map && map != "null" && setLayersApiInstance(new layersApi(map.id));
map && map != "null" && map.own_tag_space ? setTagsApi(new itemsApi<Tag>('tags', undefined, map.id)) : setTagsApi(new itemsApi<Tag>('tags'));
const map = await mapApiInstance?.getItems()
map && setMap(map)
map && map != 'null' && setLayersApiInstance(new layersApi(map.id))
map && map != 'null' && map.own_tag_space
? setTagsApi(new itemsApi<Tag>('tags', undefined, map.id))
: setTagsApi(new itemsApi<Tag>('tags'))
}
useEffect(() => {
layersApiInstance && getLayers();
layersApiInstance && getLayers()
}, [layersApiInstance])
const getLayers = async () => {
const layers = await layersApiInstance?.getItems();
layers && setLayers(layers);
setLayerPageRoutes(layers?.filter((l: any) => l.listed).map((l: any) => ({
path: '/' + l.name, // url
icon: <img src={"https://api.utopia-lab.org/assets/" + l.indexIcon}></img>,
name: l.name, // name that appear in Sidebar
})));
const layers = await layersApiInstance?.getItems()
layers && setLayers(layers)
setLayerPageRoutes(
layers
?.filter((l: any) => l.listed)
.map((l: any) => ({
path: '/' + l.name, // url
icon: <img src={'https://api.utopia-lab.org/assets/' + l.indexIcon}></img>,
name: l.name, // name that appear in Sidebar
})),
)
}
useEffect(() => {
if (map && map.name) {
document.title = map?.name && map.name;
let link = document.querySelector("link[rel~='icon']") as HTMLLinkElement
document.title = map?.name && map.name
let link: HTMLLinkElement = document.querySelector("link[rel~='icon']")!
if (!link) {
link = document.createElement('link');
link.rel = 'icon';
document.getElementsByTagName('head')[0].appendChild(link);
link = document.createElement('link')
link.rel = 'icon'
document.getElementsByTagName('head')[0].appendChild(link)
}
link.href = map?.logo && "https://api.utopia-lab.org/assets/" + map.logo; // Specify the path to your favicon
link.href = map?.logo && 'https://api.utopia-lab.org/assets/' + map.logo // Specify the path to your favicon
}
setLoading(false);
setLoading(false)
}, [map])
const currentUrl = window.location.href;
const bottomRoutes = getBottomRoutes(currentUrl);
const currentUrl = window.location.href
const bottomRoutes = getBottomRoutes(currentUrl)
if (map && layers) return (
<div className="App overflow-x-hidden">
<AuthProvider userApi={new userApi}>
<AppShell assetsApi={new assetsApi("https://api.utopia-lab.org/assets/")} appName={map.name} >
<Permissions api={permissionsApiInstance} adminRole='8ed0b24e-3320-48cd-8444-bc152304e580'></Permissions>
{tagsApi && <Tags api={tagsApi}></Tags>}
<Modal>
<ModalContent map={map} />
</Modal>
<SideBar routes={[...routes, ...layerPageRoutes]} bottomRoutes={bottomRoutes} />
<Content>
<Quests />
<Routes>
<Route path="/*" element={<MapContainer map={map} layers={layers} />}>
<Route path='login' element={<LoginPage />} />
<Route path='signup' element={<SignupPage />} />
<Route path='reset-password' element={<RequestPasswordPage resetUrl={map.url + "/set-new-password/"} />} />
<Route path='set-new-password' element={<SetNewPasswordPage />} />
<Route path="item/*" element={<ProfileView attestationApi={attestationApi} />} />
<Route path="edit-item/*" element={<ProfileForm />} />
<Route path="user-settings" element={<UserSettings />} />
<Route path="landingpage" element={<Landingpage />} />
<Route path="market" element={<MarketView />} />
<Route path="select-user" element={<SelectUser />} />
<Route path="attestation-form" element={<AttestationForm api={attestationApi} />} />
{
layers.map((l: any) =>
<Route key={l.id} path={l.name} element={<OverlayItemsIndexPage plusButton={l.index_plus_button} layerName={l.name} url={'/item/'} parameterField={'id'} />} />
)
}
</Route>
</Routes>
</Content>
</AppShell>
</AuthProvider>
</div>
)
else if (map == "null" && !loading) return (
<div className="flex items-center justify-center h-screen">
<div>
<p className='text-xl font-semibold'>This map does not exist</p>
if (map && layers)
return (
<div className='App overflow-x-hidden'>
<AuthProvider userApi={new userApi()}>
<AppShell
assetsApi={new assetsApi('https://api.utopia-lab.org/assets/')}
appName={map.name}
>
<Permissions
api={permissionsApiInstance}
adminRole='8ed0b24e-3320-48cd-8444-bc152304e580'
></Permissions>
{tagsApi && <Tags api={tagsApi}></Tags>}
<Modal>
<ModalContent map={map} />
</Modal>
<SideBar routes={[...routes, ...layerPageRoutes]} bottomRoutes={bottomRoutes} />
<Content>
<Quests />
<Routes>
<Route path='/*' element={<MapContainer map={map} layers={layers} />}>
<Route path='login' element={<LoginPage />} />
<Route path='signup' element={<SignupPage />} />
<Route
path='reset-password'
element={<RequestPasswordPage resetUrl={map.url + '/set-new-password/'} />}
/>
<Route path='set-new-password' element={<SetNewPasswordPage />} />
<Route path='item/*' element={<ProfileView attestationApi={attestationApi} />} />
<Route path='edit-item/*' element={<ProfileForm />} />
<Route path='user-settings' element={<UserSettings />} />
<Route path='landingpage' element={<Landingpage />} />
<Route path='market' element={<MarketView />} />
<Route path='select-user' element={<SelectUser />} />
<Route
path='attestation-form'
element={<AttestationForm api={attestationApi} />}
/>
{layers.map((l: any) => (
<Route
key={l.id}
path={l.name}
element={
<OverlayItemsIndexPage
plusButton={l.index_plus_button}
layerName={l.name}
url={'/item/'}
parameterField={'id'}
/>
}
/>
))}
</Route>
</Routes>
</Content>
</AppShell>
</AuthProvider>
</div>
</div>
)
else return (
<div className="outer">
<img className="pulse-loader opacity h-[96px]" src="/3markers-globe.svg" />
<br/>
<span className="loader"></span>
</div>
)
)
else if (map == 'null' && !loading)
return (
<div className='flex items-center justify-center h-screen'>
<div>
<p className='text-xl font-semibold'>This map does not exist</p>
</div>
</div>
)
else
return (
<div className='outer'>
<img className='pulse-loader opacity h-[96px]' src='/3markers-globe.svg' />
<br />
<span className='loader'></span>
</div>
)
}
export default App

View File

@ -1,147 +1,177 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/restrict-plus-operands */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable react/no-unescaped-entities */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { useState } from 'react'
import { TextView } from 'utopia-ui'
type ChapterProps = {
clickAction1?: () => void
clickAction2?: () => void
map?: any
interface ChapterProps {
clickAction1?: () => void
clickAction2?: () => void
map?: any
}
export function Welcome1({ clickAction1, map }: ChapterProps) {
return (
<>{map.custom_text?
<>
<TextView rawText={map.custom_text}></TextView>
</> :
<>
<h3 className="font-bold text-lg">Welcome to {map?.name || "Utopia Map"}</h3>
<img className="float-right w-32 m-2" src={"https://api.utopia-lab.org/assets/"+map.logo}></img>
<p className="py-3">
It is a tool for collaborative mapping to connect local initiatives, people and events.
</p>
<p className="py-1">
Join us and grow the network by adding projects and events to the map.
</p>
<p className="py-1">
Create your personal profile and place it on the map.
</p>
<div className="grid">
<label className="btn place-self-end mt-4" onClick={() => clickAction1!()}>Close</label>
</div>
</>}
return (
<>
{map.custom_text ? (
<>
<TextView rawText={map.custom_text}></TextView>
</>
)
) : (
<>
<h3 className='font-bold text-lg'>Welcome to {map?.name || 'Utopia Map'}</h3>
<img
className='float-right w-32 m-2'
src={'https://api.utopia-lab.org/assets/' + map.logo}
></img>
<p className='py-3'>
It is a tool for collaborative mapping to connect local initiatives, people and events.
</p>
<p className='py-1'>
Join us and grow the network by adding projects and events to the map.
</p>
<p className='py-1'>Create your personal profile and place it on the map.</p>
<div className='grid'>
<label className='btn place-self-end mt-4' onClick={() => clickAction1!()}>
Close
</label>
</div>
</>
)}
</>
)
}
export function Welcome2({ clickAction1 }: ChapterProps) {
return (
<>
<h3 className="font-bold text-lg"> Dencentralized Networking</h3>
<img className="float-right w-32 mx-4 my-2" src="/markers-circle.svg"></img>
return (
<>
<h3 className='font-bold text-lg'> Dencentralized Networking</h3>
<img className='float-right w-32 mx-4 my-2' src='/markers-circle.svg'></img>
<p className="py-3">
Find like-minded people, projects and events. In your neighbourhood and wherever you are!
</p>
<p className="py-3">
Onboard new people, places and events
</p>
<div className="grid">
<button className="btn place-self-end mt-4" onClick={() => clickAction1!()}>next</button>
</div>
</>
)
<p className='py-3'>
Find like-minded people, projects and events. In your neighbourhood and wherever you are!
</p>
<p className='py-3'>Onboard new people, places and events</p>
<div className='grid'>
<button className='btn place-self-end mt-4' onClick={() => clickAction1!()}>
next
</button>
</div>
</>
)
}
export function Welcome3({ clickAction1 }: ChapterProps) {
return (
<>
<h3 className="font-bold text-lg">Mapping the Change</h3>
<p className="py-3">
More and more people are waking up to what's really happening. </p>
<p className="py-1">
They are in the process of understanding the potential that is within themselves and within the whole mankind.
</p>
<img className="float-left w-32 mx-4" src="/3markers-globe.svg"></img>
return (
<>
<h3 className='font-bold text-lg'>Mapping the Change</h3>
<p className='py-3'>More and more people are waking up to what's really happening. </p>
<p className='py-1'>
They are in the process of understanding the potential that is within themselves and within
the whole mankind.
</p>
<img className='float-left w-32 mx-4' src='/3markers-globe.svg'></img>
<p className="py-1">
Starting to reconnect with our Mother Earth and beginning to question things that long times have been taken for granted.
</p>
<div className="grid">
<label className="btn place-self-end mt-4" onClick={() => clickAction1!()}>next</label>
</div>
</>
)
<p className='py-1'>
Starting to reconnect with our Mother Earth and beginning to question things that long times
have been taken for granted.
</p>
<div className='grid'>
<label className='btn place-self-end mt-4' onClick={() => clickAction1!()}>
next
</label>
</div>
</>
)
}
export function Welcome4({ clickAction1 }: ChapterProps) {
return (
<>
<h3 className="font-bold text-lg"> Dezentralized Networks </h3>
return (
<>
<h3 className='font-bold text-lg'> Dezentralized Networks </h3>
<p className="py-3">
Find like-minded people, places and events. In your neighbourhood and wherever you are!
</p>
<img className="float-right w-32 mx-4 my-2" src="/network.svg"></img>
<p className='py-3'>
Find like-minded people, places and events. In your neighbourhood and wherever you are!
</p>
<img className='float-right w-32 mx-4 my-2' src='/network.svg'></img>
<p className="py-1">
Hypnotised, they sit in front of screens in concrete blocks, flooded and disillusioned by irrelevant information.
</p>
<p className='py-1'>
Hypnotised, they sit in front of screens in concrete blocks, flooded and disillusioned by
irrelevant information.
</p>
<p className="py-1">
From an early age, they are trained to do alienated work and consume unhealthy and meaningless products.
</p>
<div className="grid">
<button className="btn place-self-end mt-4" onClick={() => clickAction1!()}>next</button>
</div>
</>
)
<p className='py-1'>
From an early age, they are trained to do alienated work and consume unhealthy and
meaningless products.
</p>
<div className='grid'>
<button className='btn place-self-end mt-4' onClick={() => clickAction1!()}>
next
</button>
</div>
</>
)
}
const close = () => {
const myModal = document.getElementById('my_modal_3') as HTMLDialogElement;
myModal.close();
const myModal = document.getElementById('my_modal_3') as HTMLDialogElement
myModal.close()
}
export const ModalContent = ({map}:{map: any}) => {
export const ModalContent = ({ map }: { map: any }) => {
const [chapter, setChapter] = useState<number>(1)
// const setQuestsOpen = useSetQuestOpen()
const [chapter, setChapter] = useState<number>(1);
//const setQuestsOpen = useSetQuestOpen()
const ActiveChapter = () => {
switch (chapter) {
case 1:
return (
<Welcome1
map={map}
clickAction1={() => {
close()
setTimeout(() => {
// setQuestsOpen(true);
setChapter(1)
}, 1000)
}}
/>
)
case 2:
return (
<Welcome2
clickAction1={() => {
setChapter(3)
}}
/>
)
case 3:
return (
<Welcome3
clickAction1={() => {
setChapter(4)
}}
/>
)
case 4:
return (
<Welcome4
clickAction1={() => {
close()
setTimeout(() => {
// setQuestsOpen(true);
setChapter(1)
}, 1000)
}}
/>
)
default:
return <></>
}
}
const ActiveChapter = () => {
switch (chapter) {
case 1:
return <Welcome1 map={map} clickAction1={() => {
close();
setTimeout(() => {
// setQuestsOpen(true);
setChapter(1);
}, 1000);
}}/>
case 2:
return <Welcome2 clickAction1={() => { setChapter(3) }} />
case 3:
return <Welcome3 clickAction1={() => { setChapter(4) }} />
case 4:
return <Welcome4 clickAction1={() => {
close();
setTimeout(() => {
// setQuestsOpen(true);
setChapter(1);
}, 1000);
}} />
default: return <></>
};
};
return (
<ActiveChapter />
)
return <ActiveChapter />
}

View File

@ -1,27 +1,28 @@
import { uploadFiles } from '@directus/sdk';
import { directusClient } from './directus';
import { AssetsApi } from 'utopia-ui';
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable no-console */
import { uploadFiles } from '@directus/sdk'
import { directusClient } from './directus'
export class assetsApi implements AssetsApi{
import type { AssetsApi } from 'utopia-ui'
url : string;
export class assetsApi implements AssetsApi {
url: string
constructor(url: string) {
this.url = url;
this.url = url
}
async upload(file:Blob, title: string) {
const formData = new FormData();
formData.append('title', title);
formData.append('file', file);
async upload(file: Blob, title: string) {
const formData = new FormData()
formData.append('title', title)
formData.append('file', file)
try {
return await directusClient.request(uploadFiles(formData));
return await directusClient.request(uploadFiles(formData))
} catch (error: any) {
console.log(error);
throw error;
console.log(error)
throw error
}
}
}
}

View File

@ -1,87 +1,93 @@
import { createDirectus, rest, authentication, AuthenticationData, AuthenticationStorage } from '@directus/sdk';
import { Point } from 'geojson'
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-return */
import { createDirectus, rest, authentication } from '@directus/sdk'
export type Place = {
id: string;
name: string;
text: string;
position?: Point;
};
import type { AuthenticationData, AuthenticationStorage } from '@directus/sdk'
import type { Point } from 'geojson'
export type Project = {
id: string;
name: string;
text: string;
position?: Point;
picture: string;
subname: string;
[key: string]: any;
};
export type Tag = {
id: string;
color: string;
};
export type Event = {
id: string;
name: string;
text: string;
position?: Point;
start: Date;
end: Date;
};
export type Update = {
id: string;
text: string;
position?: Point;
user_created: string;
date_created: string;
}
type CustomUserFields = {
position: Point;
};
export type MyCollections = {
places: Place[];
events: Event[];
updates: Update[];
tags: Tag[];
projects: Project[];
directus_users: CustomUserFields[];
};
export const authLocalStorage = (mainKey: string = "directus_storage") => ({
// implementation of get, here return json parsed data from localStorage at mainKey (or null if not found)
get: async () => {
const data = window.localStorage.getItem(mainKey);
if (data) {
return JSON.parse(data);
}
return null;
},
// implementation of set, here set the value at mainKey in localStorage, or remove it if value is null
set: async (value: AuthenticationData | null) => {
if (!value) {
return window.localStorage.removeItem(mainKey);
}
return window.localStorage.setItem(mainKey, JSON.stringify(value));
},
} as AuthenticationStorage);
export async function getRefreshToken(){
let auth = await authLocalStorage().get()
return auth!.refresh_token;
export interface Place {
id: string
name: string
text: string
position?: Point
}
export const directusClient = createDirectus<MyCollections>("https://api.utopia-lab.org/")
export interface Project {
id: string
name: string
text: string
position?: Point
picture: string
subname: string
[key: string]: any
}
export interface Tag {
id: string
color: string
}
export interface Event {
id: string
name: string
text: string
position?: Point
start: Date
end: Date
}
export interface Update {
id: string
text: string
position?: Point
user_created: string
date_created: string
}
interface CustomUserFields {
position: Point
}
export interface MyCollections {
places: Place[]
events: Event[]
updates: Update[]
tags: Tag[]
projects: Project[]
directus_users: CustomUserFields[]
}
export const authLocalStorage = (mainKey = 'directus_storage') =>
({
// implementation of get, here return json parsed data from localStorage at mainKey (or null if not found)
get: async () => {
const data = window.localStorage.getItem(mainKey)
if (data) {
return JSON.parse(data)
}
return null
},
// implementation of set, here set the value at mainKey in localStorage, or remove it if value is null
set: async (value: AuthenticationData | null) => {
if (!value) {
return window.localStorage.removeItem(mainKey)
}
return window.localStorage.setItem(mainKey, JSON.stringify(value))
},
}) as AuthenticationStorage
export async function getRefreshToken() {
const auth = await authLocalStorage().get()
return auth!.refresh_token
}
export const directusClient = createDirectus<MyCollections>('https://api.utopia-lab.org/')
.with(rest())
.with(authentication('json', { // add this if you want to use authentication, json is important, it's type of your authentication usage, here JWT
storage: authLocalStorage(), // here set the storage previously created
}));
.with(
authentication('json', {
// add this if you want to use authentication, json is important, it's type of your authentication usage, here JWT
storage: authLocalStorage(), // here set the storage previously created
}),
)

View File

@ -1,103 +1,124 @@
import { createItem, deleteItem, readItem, readItems, updateItem } from '@directus/sdk';
import { MyCollections, directusClient } from './directus';
import { ItemsApi } from 'utopia-ui';
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable no-console */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
import { createItem, deleteItem, readItem, readItems, updateItem } from '@directus/sdk'
import { directusClient } from './directus'
import type { MyCollections } from './directus'
import type { ItemsApi } from 'utopia-ui'
export class itemsApi<T> implements ItemsApi<T>{
collectionName: string;
filter: any;
layerId: string | undefined;
mapId: string | undefined;
export class itemsApi<T> implements ItemsApi<T> {
collectionName: string
filter: any
layerId: string | undefined
mapId: string | undefined
customParameter: any
constructor(collectionName: string, layerId?: string | undefined, mapId?: string | undefined, filter?: any, customParameter?:any ) {
this.collectionName = collectionName;
if(filter) this.filter = filter;
else this.filter = {};
this.layerId = layerId;
if(layerId) {
this.filter = {... filter, ... { "layer" : { "id": { "_eq": layerId }}}}
constructor(
collectionName: string,
layerId?: string | undefined,
mapId?: string | undefined,
filter?: any,
customParameter?: any,
) {
this.collectionName = collectionName
if (filter) this.filter = filter
else this.filter = {}
this.layerId = layerId
if (layerId) {
this.filter = { ...filter, ...{ layer: { id: { _eq: layerId } } } }
}
this.mapId = mapId;
if(mapId) {
this.filter = {... filter, ... { "map" : { "id": { "_eq": mapId }}}}
this.mapId = mapId
if (mapId) {
this.filter = { ...filter, ...{ map: { id: { _eq: mapId } } } }
}
if(customParameter) this.customParameter = customParameter;
if (customParameter) this.customParameter = customParameter
}
async getItems(): Promise<T[]> {
try {
const result = await directusClient.request<T[]>(
readItems(
this.collectionName as never,
{
fields: ['*', 'to.*', "relations.*", "user_created.*", { offers: ['*'], needs: ['*'], gallery: ['*.*'] } as any],
filter: this.filter,
limit: -1
}
)
);
return result as T[];
readItems(this.collectionName as never, {
fields: [
'*',
'to.*',
'relations.*',
'user_created.*',
{ offers: ['*'], needs: ['*'], gallery: ['*.*'] } as any,
],
filter: this.filter,
limit: -1,
}),
)
return result
} catch (error: any) {
console.error(error);
console.error(error)
if (error.errors?.[0]?.message) {
throw new Error(error.errors[0].message);
throw new Error(error.errors[0].message)
} else {
throw error;
throw error
}
}
}
async getItem(id : string): Promise<T> {
async getItem(id: string): Promise<T> {
try {
const result = await directusClient.request(readItem(this.collectionName as never, id));
const result = await directusClient.request(readItem(this.collectionName as never, id))
return result as T
} catch (error: any) {
console.log(error);
if (error.errors[0]?.message)
throw error.errors[0].message;
else throw error;
console.log(error)
if (error.errors[0]?.message) throw error.errors[0].message
else throw error
}
}
async createItem(item: T & { id?: string }) : Promise<T> {
async createItem(item: T & { id?: string }): Promise<T> {
try {
const result = await directusClient.request(createItem(this.collectionName as keyof MyCollections, {...item, ...(this.customParameter && this.customParameter), ...(this.layerId && {layer: this.layerId}), ...(this.layerId && {layer: this.layerId}), ...(this.mapId && {map: this.mapId})}))
const result = await directusClient.request(
createItem(this.collectionName as keyof MyCollections, {
...item,
...(this.customParameter && this.customParameter),
...(this.layerId && { layer: this.layerId }),
...(this.layerId && { layer: this.layerId }),
...(this.mapId && { map: this.mapId }),
}),
)
return result as T
} catch (error: any) {
console.log(error);
if (error.errors[0]?.message)
throw error.errors[0].message;
else throw error;
console.log(error)
if (error.errors[0]?.message) throw error.errors[0].message
else throw error
}
}
async updateItem(item: T & { id?: string }) : Promise<T> {
async updateItem(item: T & { id?: string }): Promise<T> {
try {
const result = await directusClient.request(updateItem(this.collectionName as keyof MyCollections, item.id!, item))
const result = await directusClient.request(
updateItem(this.collectionName as keyof MyCollections, item.id!, item),
)
return result as T
} catch (error: any) {
console.log(error);
if (error.errors[0].message)
throw error.errors[0].message;
else throw error;
console.log(error)
if (error.errors[0].message) throw error.errors[0].message
else throw error
}
}
async deleteItem(id: string) : Promise<boolean> {
async deleteItem(id: string): Promise<boolean> {
try {
const result = await directusClient.request(deleteItem(this.collectionName as keyof MyCollections, id))
const result = await directusClient.request(
deleteItem(this.collectionName as keyof MyCollections, id),
)
return result as unknown as boolean
} catch (error: any) {
console.log(error);
if (error.errors[0].message)
throw error.errors[0].message;
else throw error;
console.log(error)
if (error.errors[0].message) throw error.errors[0].message
else throw error
}
}
}
}

View File

@ -1,25 +1,31 @@
import { readItems } from '@directus/sdk';
import { directusClient } from './directus';
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable no-console */
import { readItems } from '@directus/sdk'
import { directusClient } from './directus'
export class layersApi {
mapId : string
mapId: string
constructor(mapId: string) {
this.mapId = mapId;
this.mapId = mapId
}
async getItems() {
try {
const layers = await directusClient.request(readItems("layers" as any, { fields: ['*', {itemType : ['*.*', {profileTemplate: ['*', 'item.*.*.*'] }]} as any], filter: { "maps": { "maps_id": { "id": { "_eq": this.mapId } } } }, limit: 500 }));
return layers;
const layers = await directusClient.request(
readItems('layers' as any, {
fields: ['*', { itemType: ['*.*', { profileTemplate: ['*', 'item.*.*.*'] }] } as any],
filter: { maps: { maps_id: { id: { _eq: this.mapId } } } },
limit: 500,
}),
)
return layers
} catch (error: any) {
console.log(error);
if (error.errors[0]?.message)
throw error.errors[0].message;
else throw error;
console.log(error)
if (error.errors[0]?.message) throw error.errors[0].message
else throw error
}
}
}

View File

@ -1,26 +1,33 @@
import { readItems } from '@directus/sdk';
import { directusClient } from './directus';
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable no-console */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { readItems } from '@directus/sdk'
import { directusClient } from './directus'
export class mapApi {
url : string
url: string
constructor(url: string) {
this.url = url;
this.url = url
}
async getItems() {
try {
const map = await directusClient.request(readItems("maps" as any, { fields: ['*', {user_type : ['name']}], filter: { "url": { "_eq": this.url } } as any, limit: 500 }));
if(map[0]) return map[0];
else return "null";
const map = await directusClient.request(
readItems('maps' as any, {
fields: ['*', { user_type: ['name'] }],
filter: { url: { _eq: this.url } } as any,
limit: 500,
}),
)
if (map[0]) return map[0]
else return 'null'
} catch (error: any) {
console.log(error);
if (error.errors[0]?.message)
throw error.errors[0].message;
else throw error;
console.log(error)
if (error.errors[0]?.message) throw error.errors[0].message
else throw error
}
}
}

View File

@ -1,6 +1,13 @@
import { readPermissions } from "@directus/sdk";
import { directusClient } from "./directus";
import { ItemsApi, Permission } from "utopia-ui";
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-useless-constructor */
/* eslint-disable @typescript-eslint/no-empty-function */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable no-console */
import { readPermissions } from '@directus/sdk'
import { directusClient } from './directus'
import type { ItemsApi, Permission } from 'utopia-ui'
export class permissionsApi implements ItemsApi<Permission> {
constructor() {}
@ -8,13 +15,13 @@ export class permissionsApi implements ItemsApi<Permission> {
async getItems(): Promise<Permission[]> {
try {
const result = await directusClient.request(
readPermissions({ fields: ["*", { policy: ["name", "roles"] } as any] })
);
readPermissions({ fields: ['*', { policy: ['name', 'roles'] } as any] }),
)
return result as unknown as Permission[]
} catch (error: any) {
console.log(error);
if (error.errors[0]?.message) throw error.errors[0].message;
else throw error;
console.log(error)
if (error.errors[0]?.message) throw error.errors[0].message
else throw error
}
}
}

View File

@ -1,19 +1,18 @@
import { readUser } from '@directus/sdk';
import { directusClient } from './directus';
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable no-console */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { readUser } from '@directus/sdk'
import { directusClient } from './directus'
export class readUserApi{
async getItem(id : string) {
export class readUserApi {
async getItem(id: string) {
try {
return await directusClient.request(readUser(id));
return await directusClient.request(readUser(id))
} catch (error: any) {
console.log(error);
if (error.errors[0]?.message)
throw error.errors[0].message;
else throw error;
console.log(error)
if (error.errors[0]?.message) throw error.errors[0].message
else throw error
}
}
}
}

View File

@ -1,24 +1,29 @@
import axios from 'axios';
import { ItemsApi } from 'utopia-ui';
/* eslint-disable no-console */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-explicit-any */
import axios from 'axios'
import type { ItemsApi } from 'utopia-ui'
export class refiBcnApi implements ItemsApi<any>{
collectionName: string;
export class refiBcnApi implements ItemsApi<any> {
collectionName: string
constructor(collectionName: string) {
this.collectionName = collectionName;
this.collectionName = collectionName
}
async getItems() {
try {
return (await axios.get('https://antontranelis.github.io/ReFi-Barcelona-Prototype/projects/index.json')).data.data;
return (
await axios.get(
'https://antontranelis.github.io/ReFi-Barcelona-Prototype/projects/index.json',
)
).data.data
} catch (error: any) {
console.log(error);
if (error.errors[0]?.message)
throw error.errors[0].message;
else throw error;
console.log(error)
if (error.errors[0]?.message) throw error.errors[0].message
else throw error
}
}
}
}

View File

@ -1,110 +1,97 @@
import { createUser, passwordRequest, passwordReset, readMe, updateMe} from '@directus/sdk';
import { directusClient } from './directus';
import { UserApi, UserItem } from 'utopia-ui';
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable camelcase */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable no-console */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unsafe-return */
import { createUser, passwordRequest, passwordReset, readMe, updateMe } from '@directus/sdk'
import { directusClient } from './directus'
import type { UserApi, UserItem } from 'utopia-ui'
export class userApi implements UserApi {
async register(email: string, password: string, userName: string): Promise<any> {
try {
return await directusClient.request(createUser({email: email, password: password, first_name: userName}));
return await directusClient.request(createUser({ email, password, first_name: userName }))
} catch (error: any) {
console.log(error);
if (error.errors[0].message)
throw error.errors[0].message;
else throw error;
console.log(error)
if (error.errors[0].message) throw error.errors[0].message
else throw error
}
}
async login(email: string, password: string): Promise<any> {
try {
return await directusClient.login(email,password,{mode: 'json'});
return await directusClient.login(email, password, { mode: 'json' })
} catch (error: any) {
console.log(error);
if (error.errors[0].message)
throw error.errors[0].message;
else throw error;
console.log(error)
if (error.errors[0].message) throw error.errors[0].message
else throw error
}
}
async logout(): Promise<any> {
try {
return await directusClient.logout();
return await directusClient.logout()
} catch (error: any) {
console.log(error);
if (error.errors[0].message)
throw error.errors[0].message;
else throw error;
console.log(error)
if (error.errors[0].message) throw error.errors[0].message
else throw error
}
}
async getUser(): Promise<any> {
try {
let user = await directusClient.request(readMe({ fields: ['*', {role: ['*']} as any] }));
return user;
const user = await directusClient.request(readMe({ fields: ['*', { role: ['*'] } as any] }))
return user
} catch (error: any) {
console.log(error);
if (error.errors[0].message)
throw error.errors[0].message;
else throw error;
console.log(error)
if (error.errors[0].message) throw error.errors[0].message
else throw error
}
}
async getToken(): Promise<any> {
async getToken(): Promise<any> {
try {
const token = await directusClient.getToken();
return token;
const token = await directusClient.getToken()
return token
} catch (error: any) {
console.log(error);
if (error.errors[0].message)
throw error.errors[0].message;
else throw error;
console.log(error)
if (error.errors[0].message) throw error.errors[0].message
else throw error
}
}
async updateUser(user: UserItem): Promise<any> {
const { id, ...userRest } = user;
const { id, ...userRest } = user
try {
const res = await directusClient.request(updateMe(userRest,{ fields: ['*'] }))
return res as any;
const res = await directusClient.request(updateMe(userRest, { fields: ['*'] }))
return res as any
} catch (error: any) {
console.log(error);
if (error.errors[0].message)
throw error.errors[0].message;
else throw error;
console.log(error)
if (error.errors[0].message) throw error.errors[0].message
else throw error
}
}
async requestPasswordReset(email:string, reset_url?:string): Promise<any> {
async requestPasswordReset(email: string, reset_url?: string): Promise<any> {
try {
return await directusClient.request(passwordRequest(email,reset_url));
return await directusClient.request(passwordRequest(email, reset_url))
} catch (error: any) {
console.log(error);
if (error.errors[0].message)
throw error.errors[0].message;
else throw error;
console.log(error)
if (error.errors[0].message) throw error.errors[0].message
else throw error
}
}
async passwordReset(reset_token:string, new_password:string): Promise<any> {
async passwordReset(reset_token: string, new_password: string): Promise<any> {
try {
return await directusClient.request(passwordReset(reset_token, new_password));
return await directusClient.request(passwordReset(reset_token, new_password))
} catch (error: any) {
console.log(error);
if (error.errors[0].message)
throw error.errors[0].message;
else throw error;
console.log(error)
if (error.errors[0].message) throw error.errors[0].message
else throw error
}
}
}

View File

@ -1,9 +1,14 @@
/* eslint-disable import/default */
/* eslint-disable import/extensions */
/* eslint-disable import/no-named-as-default-member */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>,

View File

@ -1,102 +1,98 @@
import React, { useState } from 'react'
/* eslint-disable import/default */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/24/outline'
import {
add,
eachDayOfInterval,
endOfMonth,
endOfWeek,
format,
getDay,
isSameMonth,
isToday,
parse,
startOfToday,
startOfWeek,
} from "date-fns";
import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/24/outline';
import { MapOverlayPage } from 'utopia-ui';
add,
eachDayOfInterval,
endOfMonth,
endOfWeek,
format,
getDay,
isSameMonth,
isToday,
parse,
startOfToday,
startOfWeek,
} from 'date-fns'
import React, { useState } from 'react'
import { MapOverlayPage } from 'utopia-ui'
export const Calendar = () => {
const today = startOfToday();
const days = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
const colStartClasses = [
"",
"col-start-2",
"col-start-3",
"col-start-4",
"col-start-5",
"col-start-6",
"col-start-7",
];
const [currMonth, setCurrMonth] = useState(() => format(today, "MMM-yyyy"));
let firstDayOfMonth = parse(currMonth, "MMM-yyyy", new Date());
const daysInMonth = eachDayOfInterval({
start: startOfWeek(firstDayOfMonth),
end: endOfWeek(endOfMonth(firstDayOfMonth)),
});
const getPrevMonth = (event: React.MouseEvent<SVGSVGElement>) => {
event.preventDefault();
const firstDayOfPrevMonth = add(firstDayOfMonth, { months: -1 });
setCurrMonth(format(firstDayOfPrevMonth, "MMM-yyyy"));
};
const getNextMonth = (event: React.MouseEvent<SVGSVGElement>) => {
event.preventDefault();
const firstDayOfNextMonth = add(firstDayOfMonth, { months: 1 });
setCurrMonth(format(firstDayOfNextMonth, "MMM-yyyy"));
};
return (
<MapOverlayPage backdrop className='tw-max-h-[calc(100dvh-96px)] tw-h-fit md:tw-w-[calc(50%-32px)] tw-w-[calc(100%-32px)] max-w-lg'>
<div className="flex items-center justify-between">
<p className="font-semibold text-xl">
{format(firstDayOfMonth, "MMMM yyyy")}
</p>
<div className="flex items-center justify-evenly gap-6 sm:gap-12">
<ChevronLeftIcon
className="w-6 h-6 cursor-pointer"
onClick={getPrevMonth}
/>
<ChevronRightIcon
className="w-6 h-6 cursor-pointer"
onClick={getNextMonth}
/>
const today = startOfToday()
const days = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']
const colStartClasses = [
'',
'col-start-2',
'col-start-3',
'col-start-4',
'col-start-5',
'col-start-6',
'col-start-7',
]
const [currMonth, setCurrMonth] = useState(() => format(today, 'MMM-yyyy'))
const firstDayOfMonth = parse(currMonth, 'MMM-yyyy', new Date())
const daysInMonth = eachDayOfInterval({
start: startOfWeek(firstDayOfMonth),
end: endOfWeek(endOfMonth(firstDayOfMonth)),
})
const getPrevMonth = (event: React.MouseEvent<SVGSVGElement>) => {
event.preventDefault()
const firstDayOfPrevMonth = add(firstDayOfMonth, { months: -1 })
setCurrMonth(format(firstDayOfPrevMonth, 'MMM-yyyy'))
}
const getNextMonth = (event: React.MouseEvent<SVGSVGElement>) => {
event.preventDefault()
const firstDayOfNextMonth = add(firstDayOfMonth, { months: 1 })
setCurrMonth(format(firstDayOfNextMonth, 'MMM-yyyy'))
}
return (
<MapOverlayPage
backdrop
className='tw-max-h-[calc(100dvh-96px)] tw-h-fit md:tw-w-[calc(50%-32px)] tw-w-[calc(100%-32px)] max-w-lg'
>
<div className='flex items-center justify-between'>
<p className='font-semibold text-xl'>{format(firstDayOfMonth, 'MMMM yyyy')}</p>
<div className='flex items-center justify-evenly gap-6 sm:gap-12'>
<ChevronLeftIcon className='w-6 h-6 cursor-pointer' onClick={getPrevMonth} />
<ChevronRightIcon className='w-6 h-6 cursor-pointer' onClick={getNextMonth} />
</div>
</div>
<hr className='my-6' />
<div className='grid grid-cols-7 gap-6 sm:gap-12 place-items-center'>
{days.map((day, idx) => {
return (
<div key={idx} className='font-semibold'>
{capitalizeFirstLetter(day)}
</div>
</div>
<hr className="my-6" />
<div className="grid grid-cols-7 gap-6 sm:gap-12 place-items-center">
{days.map((day, idx) => {
return (
<div key={idx} className="font-semibold">
{capitalizeFirstLetter(day)}
</div>
);
})}
</div>
<div className="grid grid-cols-7 gap-4 sm:gap-12 mt-8 place-items-center">
{daysInMonth.map((day, idx) => {
return (
<div key={idx} className={colStartClasses[getDay(day)]}>
<p
className={`cursor-pointer flex items-center justify-center font-semibold h-8 w-8 rounded-full hover:text-white ${
isSameMonth(day, today) ? "text-current" : "text-gray-500"
} ${!isToday(day) && "hover:bg-primary-content"} ${
isToday(day) && "bg-primary !text-white"
}`}
>
{format(day, "d")}
</p>
</div>
);
})}
</div>
</MapOverlayPage>
);
)
})}
</div>
<div className='grid grid-cols-7 gap-4 sm:gap-12 mt-8 place-items-center'>
{daysInMonth.map((day, idx) => {
return (
<div key={idx} className={colStartClasses[getDay(day)]}>
<p
className={`cursor-pointer flex items-center justify-center font-semibold h-8 w-8 rounded-full hover:text-white ${
isSameMonth(day, today) ? 'text-current' : 'text-gray-500'
} ${!isToday(day) && 'hover:bg-primary-content'} ${
isToday(day) && 'bg-primary !text-white'
}`}
>
{format(day, 'd')}
</p>
</div>
)
})}
</div>
</MapOverlayPage>
)
}
const capitalizeFirstLetter = (string: string) => {
return string
}
return string
}

View File

@ -1,120 +1,109 @@
import { CardPage } from "utopia-ui"
/* eslint-disable react/no-unescaped-entities */
import { CardPage } from 'utopia-ui'
export default function Concept() {
return (
<CardPage title="Concept">
Utopia is a cooperative Real Life Manifestation Game. While playing, we connect with ourselves, each other and our dreams to manifest them together.<br></br><br></br>
<div className="collapse collapse-arrow bg-base-200 mb-2">
<input type="radio" name="my-accordion-2" />
<div className="collapse-title text-xl font-medium">
Real Life Manifestation Games </div>
<div className="collapse-content">
<ul className="list-disc list-inside pl-4">
<li>
Like a role-playing game, you can create your own profile, but here you can map, share and train real skills.
<CardPage title='Concept'>
Utopia is a cooperative Real Life Manifestation Game. While playing, we connect with
ourselves, each other and our dreams to manifest them together.<br></br>
<br></br>
<div className='collapse collapse-arrow bg-base-200 mb-2'>
<input type='radio' name='my-accordion-2' />
<div className='collapse-title text-xl font-medium'>Real Life Manifestation Games </div>
<div className='collapse-content'>
<ul className='list-disc list-inside pl-4'>
<li>
Like a role-playing game, you can create your own profile, but here you can map, share
and train real skills.
</li>
<li>
Further, real resources are made visible and available, managed and used, similar to a strategy game.
<li>
Further, real resources are made visible and available, managed and used, similar to a
strategy game.
</li>
<li>
Project management tasks can be mapped as quests, levels, missions and problems become challenges.
<li>
Project management tasks can be mapped as quests, levels, missions and problems become
challenges.
</li>
<li>
The storytelling is based on the real conditions and challenges on our planet.
</li>
<li>
The goal of the game is to create win-win-win situations. Win for you, win for us, win for the world.
<li>The storytelling is based on the real conditions and challenges on our planet.</li>
<li>
The goal of the game is to create win-win-win situations. Win for you, win for us, win
for the world.
</li>
</ul>
</div>
</div>
<div className="collapse collapse-arrow bg-base-200 mb-2">
<input type="radio" name="my-accordion-2" />
<div className="collapse-title text-xl font-medium">
Elements
</div>
<div className="collapse-content">
<div className="flex flex-row flex-wrap">
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<h3 className="text-base my-3 font-medium"> The App</h3>
The app provides an interactive geographical map as a playing field. It also allows you to create and view player profiles. The marketplace shows offers and needs. </div>
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<h3 className="text-base my-3 font-medium"> Print Material</h3>
To complement offline play, there are flyers, stickers, signs and workbooks that invite players to play.
Players receive or print ID cards with QR codes that are used for networking (more on this later).
<div className='collapse collapse-arrow bg-base-200 mb-2'>
<input type='radio' name='my-accordion-2' />
<div className='collapse-title text-xl font-medium'>Elements</div>
<div className='collapse-content'>
<div className='flex flex-row flex-wrap'>
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<h3 className='text-base my-3 font-medium'> The App</h3>
The app provides an interactive geographical map as a playing field. It also allows
you to create and view player profiles. The marketplace shows offers and needs.{' '}
</div>
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<h3 className="text-base my-3 font-medium"> Gatherings</h3>
Coming together at workshops, festivals and local meetings to connect, build structures and to engage new players. </div>
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<h3 className="text-base my-3 font-medium"> Permanent Structures</h3>
When we play, we create tangible structures like places and infrastructure.
And also intangibles like networks of relationships, stories, information ...
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<h3 className='text-base my-3 font-medium'> Print Material</h3>
To complement offline play, there are flyers, stickers, signs and workbooks that
invite players to play. Players receive or print ID cards with QR codes that are used
for networking (more on this later).
</div>
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<h3 className='text-base my-3 font-medium'> Gatherings</h3>
Coming together at workshops, festivals and local meetings to connect, build
structures and to engage new players.{' '}
</div>
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<h3 className='text-base my-3 font-medium'> Permanent Structures</h3>
When we play, we create tangible structures like places and infrastructure. And also
intangibles like networks of relationships, stories, information ...
</div>
</div>
</div>
</div>
<div className="collapse collapse-arrow bg-base-200 mb-2">
<input type="radio" name="my-accordion-2" />
<div className="collapse-title text-xl font-medium">
Goals </div>
<div className="collapse-content">
<ol className="list-decimal list-inside pl-4">
<div className='collapse collapse-arrow bg-base-200 mb-2'>
<input type='radio' name='my-accordion-2' />
<div className='collapse-title text-xl font-medium'>Goals </div>
<div className='collapse-content'>
<ol className='list-decimal list-inside pl-4'>
<li>To build a decentralised network</li>
<li>Free development of our collective and individual potential</li>
<li>Start co-creation and build collective structures</li>
</ol>
</div>
</div>
<div className="collapse collapse-arrow bg-base-200 mb-2">
<input type="radio" name="my-accordion-2" />
<div className="collapse-title text-xl font-medium">
Gameplay </div>
<div className="collapse-content">
Through playful elements and gamification, the player is motivated and guided through quests and levels.
<h3 className="text-base my-3 font-medium"> Player Profiles</h3>
The player examines himself and his abilities as well as deeper desires and visions to define his character or player profile.
The focus is on the following questions:
<ul className="list-disc list-inside pl-4 pt-4">
<div className='collapse collapse-arrow bg-base-200 mb-2'>
<input type='radio' name='my-accordion-2' />
<div className='collapse-title text-xl font-medium'>Gameplay </div>
<div className='collapse-content'>
Through playful elements and gamification, the player is motivated and guided through
quests and levels.
<h3 className='text-base my-3 font-medium'> Player Profiles</h3>
The player examines himself and his abilities as well as deeper desires and visions to
define his character or player profile. The focus is on the following questions:
<ul className='list-disc list-inside pl-4 pt-4'>
<li>How and in what kind of world do I want to live?</li>
<li>Who am I and what are my special abilities or my special task in this life?</li>
<li>What do I have to give? What can and do I want to share with others and the world?</li>
<li>What do I still need to come fully into my power? How can others support me in this?</li>
<li>
What do I have to give? What can and do I want to share with others and the world?
</li>
<li>
What do I still need to come fully into my power? How can others support me in this?
</li>
</ul>
<h3 className="text-base my-3 font-medium"> Resources</h3>
The player explores and defines his/her offers and needs, shares his/her resources and uses those of the network.
E.g. tools, machines, electrical appliances, vehicles, food and drink, places to sleep, rides, books, access to the internet, individual skills and help in everyday life
<h3 className="text-base my-3 font-medium"> Realising Projects</h3>
The player joins projects and starts his own.
The game offers support in project management or crowdfunding.
In this way, structures, events, permanent places, infrastructure and everything we need to meet our human needs in harmony with Mother Earth can be created.
<h3 className="text-base my-3 font-medium"> Making Change visible</h3>
<h3 className='text-base my-3 font-medium'> Resources</h3>
The player explores and defines his/her offers and needs, shares his/her resources and
uses those of the network. E.g. tools, machines, electrical appliances, vehicles, food and
drink, places to sleep, rides, books, access to the internet, individual skills and help
in everyday life
<h3 className='text-base my-3 font-medium'> Realising Projects</h3>
The player joins projects and starts his own. The game offers support in project
management or crowdfunding. In this way, structures, events, permanent places,
infrastructure and everything we need to meet our human needs in harmony with Mother Earth
can be created.
<h3 className='text-base my-3 font-medium'> Making Change visible</h3>
The player is motivated to map and document the newly emerging world by ...
<ul className="list-disc list-inside pl-4 pt-4">
<ul className='list-disc list-inside pl-4 pt-4'>
<li>adding places, events etc. to the map</li>
<li>documenting projects with text, images, audio and video</li>
@ -123,106 +112,77 @@ export default function Concept() {
</ul>
</div>
</div>
<div className="collapse collapse-arrow bg-base-200 mb-2">
<input type="radio" name="my-accordion-2" />
<div className="collapse-title text-xl font-medium">
Web of Trust </div>
<div className="collapse-content">
<div className="flex flex-row flex-wrap">
While we connect with other people in real life and build our personal network, we are simultaneously exchanging cryptographic keys and building a "Web of Trust". <div className="basis-full">
<div className="divider divider-vertical"></div>
<div className='collapse collapse-arrow bg-base-200 mb-2'>
<input type='radio' name='my-accordion-2' />
<div className='collapse-title text-xl font-medium'>Web of Trust </div>
<div className='collapse-content'>
<div className='flex flex-row flex-wrap'>
While we connect with other people in real life and build our personal network, we are
simultaneously exchanging cryptographic keys and building a "Web of Trust".{' '}
<div className='basis-full'>
<div className='divider divider-vertical'></div>
</div>
<h3 className="text-base basis-full my-3 font-medium"> Decentralised IDs</h3>
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<img className="float-right h-28 px-4 pb-4" src="/public-private-key.svg"></img>
<p>When we create our profile, a key pair consisting of a private key and a public key is generated at the same time.</p>
<h3 className='text-base basis-full my-3 font-medium'> Decentralised IDs</h3>
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<img className='float-right h-28 px-4 pb-4' src='/public-private-key.svg'></img>
<p>
When we create our profile, a key pair consisting of a private key and a public key
is generated at the same time.
</p>
</div>
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<p className="basis-full pb-4">We share the public key with our friends and they can use it to encrypt data for us. We keep the private key secret. It is needed to decrypt data that has been encrypted for us on our device.</p>
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<p className='basis-full pb-4'>
We share the public key with our friends and they can use it to encrypt data for us.
We keep the private key secret. It is needed to decrypt data that has been encrypted
for us on our device.
</p>
</div>
<div className="basis-full">
<div className="divider divider-vertical"></div>
<div className='basis-full'>
<div className='divider divider-vertical'></div>
</div>
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<h3 className="text-base my-3 font-medium"> Key Exchange</h3>
<img className="float-left h-32 px-4" src="/qr-scan.svg"></img>
When we meet people in real life, we can exchange our public keys by scanning each other's QR codes or on paper. </div>
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<h3 className="text-base my-3 font-medium"> Private data sharing</h3>
<img className="float-right h-32 px-4" src="/web-of-trust.svg"></img>
Within our network, we can then share our profiles, offers, needs, projects, locations and events end-to-end encrypted.
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<h3 className='text-base my-3 font-medium'> Key Exchange</h3>
<img className='float-left h-32 px-4' src='/qr-scan.svg'></img>
When we meet people in real life, we can exchange our public keys by scanning each
other's QR codes or on paper.{' '}
</div>
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<h3 className='text-base my-3 font-medium'> Private data sharing</h3>
<img className='float-right h-32 px-4' src='/web-of-trust.svg'></img>
Within our network, we can then share our profiles, offers, needs, projects, locations
and events end-to-end encrypted.
</div>
</div>
</div>
</div>
<div className="collapse collapse-arrow bg-base-200 mb-2">
<input type="radio" name="my-accordion-2" />
<h2 className="collapse-title text-xl font-medium">
Principles </h2>
<div className="collapse-content">
<div className="flex flex-row flex-wrap">
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<h3 className="text-base my-3 font-medium"> Everything is just a game</h3>
Everything happens voluntarily, out of ourselves in the flow. Everyone is invited to join in at any time.
<div className='collapse collapse-arrow bg-base-200 mb-2'>
<input type='radio' name='my-accordion-2' />
<h2 className='collapse-title text-xl font-medium'>Principles </h2>
<div className='collapse-content'>
<div className='flex flex-row flex-wrap'>
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<h3 className='text-base my-3 font-medium'> Everything is just a game</h3>
Everything happens voluntarily, out of ourselves in the flow. Everyone is invited to
join in at any time.
</div>
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<h3 className="text-base my-3 font-medium"> No Money</h3>
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<h3 className='text-base my-3 font-medium'> No Money</h3>
Since the fun stops with money, we don't pay each other money when we play.
</div>
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<h3 className="text-base my-3 font-medium"> Decentralised Structures</h3>
All structures we create are decentralised and free of hierarchies. The same rules apply to everyone.
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<h3 className='text-base my-3 font-medium'> Decentralised Structures</h3>
All structures we create are decentralised and free of hierarchies. The same rules
apply to everyone.
</div>
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<h3 className='text-base my-3 font-medium'> Real Life</h3>
Real change happens in real life. We only use the internet where it directly helps to
organise real encounters.{' '}
</div>
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<h3 className="text-base my-3 font-medium"> Real Life</h3>
Real change happens in real life. We only use the internet where it directly helps to organise real encounters. </div>
</div>
</div>
</div>
</CardPage>
)
}

View File

@ -1,130 +1,118 @@
import { CardPage } from "utopia-ui"
import { CardPage } from 'utopia-ui'
export default function Concept() {
return (
<CardPage title="Concept">
<b>Utopia Game</b> is a cooperative <b>Real Life Manifestation Game</b>. While playing, we connect with ourselves, each other and our dreams to manifest them together.<br></br><br></br>
<div className="collapse collapse-arrow bg-base-200 mb-2">
<input type="radio" name="my-accordion-2" />
<div className="collapse-title text-xl font-medium">
Real-Life-Manifestations-Spiel ? </div>
<div className="collapse-content">
<div className="flex flex-row flex-wrap">
<div className="basis-full pr-4 pb-4">
Ähnlich wie bei einem Rollenspiel kann man sich sein eigenes Profil erstellen, mit dem Unterschied, dass hier echte Fähigkeiten abgebildet, geteilt und trainiert werden können.
<CardPage title='Concept'>
<b>Utopia Game</b> is a cooperative <b>Real Life Manifestation Game</b>. While playing, we
connect with ourselves, each other and our dreams to manifest them together.<br></br>
<br></br>
<div className='collapse collapse-arrow bg-base-200 mb-2'>
<input type='radio' name='my-accordion-2' />
<div className='collapse-title text-xl font-medium'>Real-Life-Manifestations-Spiel ? </div>
<div className='collapse-content'>
<div className='flex flex-row flex-wrap'>
<div className='basis-full pr-4 pb-4'>
Ähnlich wie bei einem Rollenspiel kann man sich sein eigenes Profil erstellen, mit dem
Unterschied, dass hier echte Fähigkeiten abgebildet, geteilt und trainiert werden
können.
</div>
<div className="basis-full pr-4 pb-4">
Die Geschichte und das Storytelling orientiert sich an den realen Zuständen und Herausforderungen auf unserer Erde.
<div className='basis-full pr-4 pb-4'>
Die Geschichte und das Storytelling orientiert sich an den realen Zuständen und
Herausforderungen auf unserer Erde.
</div>
<div className="basis-full pr-4 pb-4">
Des weiteren werden reale Ressourcen sichtbar und verfügbar gemacht, verwaltet und eingesetzt, ähnlich wie bei einem Strategie-Spiel.
<div className='basis-full pr-4 pb-4'>
Des weiteren werden reale Ressourcen sichtbar und verfügbar gemacht, verwaltet und
eingesetzt, ähnlich wie bei einem Strategie-Spiel.
</div>
<div className="basis-full pr-4 pb-4">
Die Aufgaben des Projektmanagements können als Quests bzw. Spielaufträge abgebildet werden. Kleine und große Aufgaben werden in Abenteuer und Herausforderungen verwandelt und Probleme in Rätsel.
<div className='basis-full pr-4 pb-4'>
Die Aufgaben des Projektmanagements können als Quests bzw. Spielaufträge abgebildet
werden. Kleine und große Aufgaben werden in Abenteuer und Herausforderungen verwandelt
und Probleme in Rätsel.
</div>
</div>
<div className="basis-full pr-4 pb-4">
Ziel des Spiels ist es, WinWinWin Situationen zu erzeugen. Win für Dich, Win für Uns, Win für die Welt.
<div className='basis-full pr-4 pb-4'>
Ziel des Spiels ist es, WinWinWin Situationen zu erzeugen. Win für Dich, Win für Uns,
Win für die Welt.
</div>
</div>
</div>
<div className="collapse collapse-arrow bg-base-200 mb-2">
<input type="radio" name="my-accordion-2" />
<div className="collapse-title text-xl font-medium">
Elemente des Spiels </div>
<div className="collapse-content">
<div className="flex flex-row flex-wrap">
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<h3 className="text-base my-3 font-medium"> App</h3>
Die App bietet eine interaktive geografische Karte als Spielfeld. Außerdem ermöglicht sie das Erstellen und Ansehen von Spieler-Profilen. Der Marktplatz zeigt Angebote und Bedürfnisse
<div className='collapse collapse-arrow bg-base-200 mb-2'>
<input type='radio' name='my-accordion-2' />
<div className='collapse-title text-xl font-medium'>Elemente des Spiels </div>
<div className='collapse-content'>
<div className='flex flex-row flex-wrap'>
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<h3 className='text-base my-3 font-medium'> App</h3>
Die App bietet eine interaktive geografische Karte als Spielfeld. Außerdem ermöglicht
sie das Erstellen und Ansehen von Spieler-Profilen. Der Marktplatz zeigt Angebote und
Bedürfnisse
</div>
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<h3 className="text-base my-3 font-medium"> Print Material</h3>
Als Ergänzung und zum Offline-Spielen gibt es Flyer, Aufkleber, Schilder und Workbooks, welche zum Spielen einladen.
Spieler erhalten Ausweise und Visitenkarten mit QR-Codes, die zu Vernetzung genutzt werden (später mehr)
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<h3 className='text-base my-3 font-medium'> Print Material</h3>
Als Ergänzung und zum Offline-Spielen gibt es Flyer, Aufkleber, Schilder und
Workbooks, welche zum Spielen einladen. Spieler erhalten Ausweise und Visitenkarten
mit QR-Codes, die zu Vernetzung genutzt werden (später mehr)
</div>
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<h3 className="text-base my-3 font-medium"> Temporäre Events</h3>
Wir kommen zusammen bei Workshops, auf Festivals und bei lokalen Treffen um uns zu connecten, Strukturen zu bilden und neue Spieler zu gewinnen.
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<h3 className='text-base my-3 font-medium'> Temporäre Events</h3>
Wir kommen zusammen bei Workshops, auf Festivals und bei lokalen Treffen um uns zu
connecten, Strukturen zu bilden und neue Spieler zu gewinnen.
</div>
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<h3 className="text-base my-3 font-medium"> Dauerhafte Strukturen</h3>
Beim Spielen erschaffen wir dauerhafte materielle Strukturen wie Orte und Infrastruktur.
Und Immaterielles wie Netzwerke aus Beziehungen, Geschichten, Informationen ...
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<h3 className='text-base my-3 font-medium'> Dauerhafte Strukturen</h3>
Beim Spielen erschaffen wir dauerhafte materielle Strukturen wie Orte und
Infrastruktur. Und Immaterielles wie Netzwerke aus Beziehungen, Geschichten,
Informationen ...
</div>
</div>
</div>
</div>
<div className="collapse collapse-arrow bg-base-200 mb-2">
<input type="radio" name="my-accordion-2" />
<div className="collapse-title text-xl font-medium">
Ziel des Spiels </div>
<div className="collapse-content">
<ol className="list-decimal list-inside pl-4">
<div className='collapse collapse-arrow bg-base-200 mb-2'>
<input type='radio' name='my-accordion-2' />
<div className='collapse-title text-xl font-medium'>Ziel des Spiels </div>
<div className='collapse-content'>
<ol className='list-decimal list-inside pl-4'>
<li>Ein dezentrales Netzwerk aufspannen</li>
<li>Freie Entfaltung unserer kollektiven und individuellen Potentiale</li>
<li>Aufbau kollektiver Strukturen und Co-Kreation</li>
</ol>
</div>
</div>
<div className="collapse collapse-arrow bg-base-200 mb-2">
<input type="radio" name="my-accordion-2" />
<div className="collapse-title text-xl font-medium">
Ablauf </div>
<div className="collapse-content">
<div className='collapse collapse-arrow bg-base-200 mb-2'>
<input type='radio' name='my-accordion-2' />
<div className='collapse-title text-xl font-medium'>Ablauf </div>
<div className='collapse-content'>
Durch spielerische Elemente / Gamification wird der Spieler motiviert und angeleitet ...
<h3 className="text-base my-3 font-medium"> Spieler Profile</h3>
Der Spieler setzt sich mit sich selbst und seinen Fähigkeiten sowie tieferen Wünschen und Visionen auseinander und definiert seinen Charakter bzw. Spieler-Profil
Dabei stehen folgende Fragen im Mittelpunkt:
<ul className="list-disc list-inside pl-4 pt-4">
<h3 className='text-base my-3 font-medium'> Spieler Profile</h3>
Der Spieler setzt sich mit sich selbst und seinen Fähigkeiten sowie tieferen Wünschen und
Visionen auseinander und definiert seinen Charakter bzw. Spieler-Profil Dabei stehen
folgende Fragen im Mittelpunkt:
<ul className='list-disc list-inside pl-4 pt-4'>
<li>Wie und in was für einer Welt möchte ich leben?</li>
<li>Wer bin ich und was sind meine besonderen Fähigkeiten oder meine spezielle Aufgabe in diesem Leben?</li>
<li>
Wer bin ich und was sind meine besonderen Fähigkeiten oder meine spezielle Aufgabe in
diesem Leben?
</li>
<li>Was habe ich zu geben? Was kann und möchte ich mit anderen und der Welt teilen?</li>
<li>Was brauche ich noch um ganz in meine Kraft zu kommen? Wie können mich andere dabei unterstützen?</li>
<li>
Was brauche ich noch um ganz in meine Kraft zu kommen? Wie können mich andere dabei
unterstützen?
</li>
</ul>
<h3 className="text-base my-3 font-medium"> Ressourcen</h3>
Der Spieler erforscht und definiert seine Angebote und Bedürfnisse, teilt seine Ressourcen und nutzt die des Netzwerks
Z.B. Werkzeuge, Maschinen, Elektrogeräte, Fahrzeuge, Essen und Trinken, Schlafplätze, Mitfahrten, Bücher, Zugang zum Internet, individuelle Fähigkeiten sowie Hilfe im Alltag
<h3 className="text-base my-3 font-medium"> Projekte umsetzen</h3>
Der Spieler schließt sich Projekten an und startet selbst welche.
Das Spiel unterstützt beim Projektmanagement oder beim Crowdfunding
So entstehen Strukturen, Veranstaltungen, dauerhafte Orte, Infrastruktur und alles was wir brauchen um unsere menschlichen Bedürfnisse im Einklang mit Mutter Erde zu befriedigen.
<h3 className="text-base my-3 font-medium"> Wandel sichtbar machen</h3>
Der Spieler wird motiviert die neu entstehende Welt zu kartieren und zu dokumentieren indem er ...
<ul className="list-disc list-inside pl-4 pt-4">
<h3 className='text-base my-3 font-medium'> Ressourcen</h3>
Der Spieler erforscht und definiert seine Angebote und Bedürfnisse, teilt seine Ressourcen
und nutzt die des Netzwerks Z.B. Werkzeuge, Maschinen, Elektrogeräte, Fahrzeuge, Essen und
Trinken, Schlafplätze, Mitfahrten, Bücher, Zugang zum Internet, individuelle Fähigkeiten
sowie Hilfe im Alltag
<h3 className='text-base my-3 font-medium'> Projekte umsetzen</h3>
Der Spieler schließt sich Projekten an und startet selbst welche. Das Spiel unterstützt
beim Projektmanagement oder beim Crowdfunding So entstehen Strukturen, Veranstaltungen,
dauerhafte Orte, Infrastruktur und alles was wir brauchen um unsere menschlichen
Bedürfnisse im Einklang mit Mutter Erde zu befriedigen.
<h3 className='text-base my-3 font-medium'> Wandel sichtbar machen</h3>
Der Spieler wird motiviert die neu entstehende Welt zu kartieren und zu dokumentieren
indem er ...
<ul className='list-disc list-inside pl-4 pt-4'>
<li>Orte, Veranstaltungen usw. in der Karte einträgt</li>
<li>Projekte mit Text, Bild und Ton dokumentiert</li>
@ -133,117 +121,83 @@ export default function Concept() {
</ul>
</div>
</div>
<div className="collapse collapse-arrow bg-base-200 mb-2">
<input type="radio" name="my-accordion-2" />
<div className="collapse-title text-xl font-medium">
Web of Trust </div>
<div className="collapse-content">
<div className="flex flex-row flex-wrap">
Während wir uns mit anderen Menschen im echten Leben connecten und unser persönliches Netzwerk aufbauen, tauschen wir gleichzeitig kryptografische Schlüssel aus und bauen ein sogenanntes Web of Trust.
<div className="basis-full">
<div className="divider divider-vertical"></div>
<div className='collapse collapse-arrow bg-base-200 mb-2'>
<input type='radio' name='my-accordion-2' />
<div className='collapse-title text-xl font-medium'>Web of Trust </div>
<div className='collapse-content'>
<div className='flex flex-row flex-wrap'>
Während wir uns mit anderen Menschen im echten Leben connecten und unser persönliches
Netzwerk aufbauen, tauschen wir gleichzeitig kryptografische Schlüssel aus und bauen ein
sogenanntes Web of Trust.
<div className='basis-full'>
<div className='divider divider-vertical'></div>
</div>
<h3 className="text-base basis-full my-3 font-medium"> Dezentrale IDs</h3>
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<img className="float-right h-24 px-4" src="/public-private-key.svg"></img>
<p>Wenn wir unser Profil erstellen wird gleichzeitig ein Schlüsselpaar bestehend aus einem privaten und einem öffentlichen Schlüssel erzeugt.</p>
<h3 className='text-base basis-full my-3 font-medium'> Dezentrale IDs</h3>
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<img className='float-right h-24 px-4' src='/public-private-key.svg'></img>
<p>
Wenn wir unser Profil erstellen wird gleichzeitig ein Schlüsselpaar bestehend aus
einem privaten und einem öffentlichen Schlüssel erzeugt.
</p>
</div>
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<p className="basis-full pb-4">Den öffentlichen Schlüssel teilen wir mit unseren Freunden und diese können damit Daten für uns verschlüsseln.</p>
<p className="basis-full">Den privaten Schlüssel halten wir geheim. Er wird benötigt um Daten die für uns verschlüsselt wurden auf unserem Gerät wieder zu entschlüsselt.</p>
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<p className='basis-full pb-4'>
Den öffentlichen Schlüssel teilen wir mit unseren Freunden und diese können damit
Daten für uns verschlüsseln.
</p>
<p className='basis-full'>
Den privaten Schlüssel halten wir geheim. Er wird benötigt um Daten die für uns
verschlüsselt wurden auf unserem Gerät wieder zu entschlüsselt.
</p>
</div>
<div className="basis-full">
<div className="divider divider-vertical"></div>
<div className='basis-full'>
<div className='divider divider-vertical'></div>
</div>
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<h3 className="text-base my-3 font-medium"> Schlüsseltausch</h3>
<img className="float-left h-32 px-4" src="/qr-scan.svg"></img>
Wenn wir Menschen im echten Leben begegnen tauschen wir unsere öffentlichen Schlüssel via QR-Code-Scan oder auf Papier.
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<h3 className='text-base my-3 font-medium'> Schlüsseltausch</h3>
<img className='float-left h-32 px-4' src='/qr-scan.svg'></img>
Wenn wir Menschen im echten Leben begegnen tauschen wir unsere öffentlichen Schlüssel
via QR-Code-Scan oder auf Papier.
</div>
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<h3 className="text-base my-3 font-medium"> Private Daten teilen</h3>
<img className="float-right h-32 px-4" src="/web-of-trust.svg"></img>
Innerhalb unseres Netzwerkes können wir dann unsere Profile, Angebote, Bedürfnisse, Projekte, Orte und Veranstaltungen ende-zu-ende-verschlüsselt teilen.
</div>
</div>
</div>
</div>
<div className="collapse collapse-arrow bg-base-200 mb-2">
<input type="radio" name="my-accordion-2" />
<h2 className="collapse-title text-xl font-semibold">
Prinzipien </h2>
<div className="collapse-content">
<div className="flex flex-row flex-wrap">
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<h3 className="text-base my-3 font-medium"> Alles ist nur ein Spiel</h3>
Alles passiert freiwillig, aus uns selbst heraus im Flow. Jeder ist jederzeit eingeladen mitzumachen.
</div>
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<h3 className="text-base my-3 font-medium"> Kein Geld</h3>
Da beim Geld der Spaß bekanntlich aufhört, bezahlen wir uns beim Spielen gegenseitig kein Geld.
</div>
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<h3 className="text-base my-3 font-medium"> Dezentrale Strukturen</h3>
Alle Strukturen, die wir erschaffen, gestalten wir dezentral und frei von Hierarchien. Für alle gelten die gleichen Regeln.
</div>
<div className="basis-full md:basis-1/2 pr-4 pb-4">
<h3 className="text-base my-3 font-medium"> Real Life</h3>
Veränderung passiert im echten Leben. Wir nutzen das Internet nur wo es direkt hilft echte Begegnungen zu organisieren.
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<h3 className='text-base my-3 font-medium'> Private Daten teilen</h3>
<img className='float-right h-32 px-4' src='/web-of-trust.svg'></img>
Innerhalb unseres Netzwerkes können wir dann unsere Profile, Angebote, Bedürfnisse,
Projekte, Orte und Veranstaltungen ende-zu-ende-verschlüsselt teilen.
</div>
</div>
</div>
</div>
<div className='collapse collapse-arrow bg-base-200 mb-2'>
<input type='radio' name='my-accordion-2' />
<h2 className='collapse-title text-xl font-semibold'>Prinzipien </h2>
<div className='collapse-content'>
<div className='flex flex-row flex-wrap'>
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<h3 className='text-base my-3 font-medium'> Alles ist nur ein Spiel</h3>
Alles passiert freiwillig, aus uns selbst heraus im Flow. Jeder ist jederzeit
eingeladen mitzumachen.
</div>
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<h3 className='text-base my-3 font-medium'> Kein Geld</h3>
Da beim Geld der Spaß bekanntlich aufhört, bezahlen wir uns beim Spielen gegenseitig
kein Geld.
</div>
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<h3 className='text-base my-3 font-medium'> Dezentrale Strukturen</h3>
Alle Strukturen, die wir erschaffen, gestalten wir dezentral und frei von Hierarchien.
Für alle gelten die gleichen Regeln.
</div>
<div className='basis-full md:basis-1/2 pr-4 pb-4'>
<h3 className='text-base my-3 font-medium'> Real Life</h3>
Veränderung passiert im echten Leben. Wir nutzen das Internet nur wo es direkt hilft
echte Begegnungen zu organisieren.
</div>
</div>
</div>
</div>
</CardPage>
)
}

View File

@ -1,195 +1,216 @@
import { useEffect, useState } from 'react';
/* eslint-disable react/no-unescaped-entities */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable import/no-relative-parent-imports */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable new-cap */
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable @typescript-eslint/no-floating-promises */
import { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { MapOverlayPage } from 'utopia-ui'
import { itemsApi } from '../api/itemsApi';
import { itemsApi } from '../api/itemsApi'
export const Landingpage = () => {
const [isLandingpageVisible, setIsLandingpageVisible] = useState(true)
const [isBoxVisible, setIsBoxVisible] = useState(true)
const [isPhoneVisible, setIsPhoneVisible] = useState(true)
const [isLandingpageVisible, setIsLandingpageVisible] = useState(true);
const [isBoxVisible, setIsBoxVisible] = useState(true);
const [isPhoneVisible, setIsPhoneVisible] = useState(true);
const [featuresApi, setFeaturesApi] = useState<itemsApi<any>>()
const [features, setFeatures] = useState<any[]>()
const [featuresApi, setFeaturesApi] = useState<itemsApi<any>>();
const [features, setFeatures] = useState<any[]>();
const [teamApi, setTeamApi] = useState<itemsApi<any>>();
const [team, setTeam] = useState<any[]>();
const [teamApi, setTeamApi] = useState<itemsApi<any>>()
const [team, setTeam] = useState<any[]>()
const loadFeatures = async () => {
const items = await featuresApi?.getItems();
setFeatures(items as any);
const items = await featuresApi?.getItems()
setFeatures(items as any)
}
const loadTeam = async () => {
const items = await teamApi?.getItems();
setTeam(items as any);
const items = await teamApi?.getItems()
setTeam(items as any)
}
useEffect(() => {
setFeaturesApi(new itemsApi<any>('features',undefined, undefined, {"status":{"_eq": "published"}}));
setTeamApi(new itemsApi<any>('team'));
loadTeam();
loadFeatures();
setFeaturesApi(
new itemsApi<any>('features', undefined, undefined, { status: { _eq: 'published' } }),
)
setTeamApi(new itemsApi<any>('team'))
loadTeam()
loadFeatures()
}, [])
useEffect(() => {
loadFeatures();
loadFeatures()
}, [featuresApi])
useEffect(() => {
loadTeam();
loadTeam()
}, [teamApi])
const navigate = useNavigate();
const navigate = useNavigate()
const startGame = () => {
setTimeout(() => {
setIsBoxVisible(false)
}, 200
)
}, 200)
setTimeout(() => {
setIsPhoneVisible(false)
}, 200
)
}, 200)
setTimeout(() => {
setIsLandingpageVisible(false)
}, 500
)
}, 500)
setTimeout(() => {
navigate("/")
}, 1500
)
navigate('/')
}, 1500)
}
return (
<MapOverlayPage className={`!rounded-none overflow-y-auto !p-0 fadeable-div flex-none ${isLandingpageVisible ? '' : 'div-hidden'}`} card={false}>
<div className="hero min-h-full text-base">
<div className="hero-content text-center flex flex-col place-items-center p-0" >
<div className='bg-no-repeat bg-center w-full' style={{ backgroundImage: "url(bg1.webp)" }}>
<MapOverlayPage
className={`!rounded-none overflow-y-auto !p-0 fadeable-div flex-none ${isLandingpageVisible ? '' : 'div-hidden'}`}
card={false}
>
<div className='hero min-h-full text-base'>
<div className='hero-content text-center flex flex-col place-items-center p-0'>
<div
className='bg-no-repeat bg-center w-full'
style={{ backgroundImage: 'url(bg1.webp)' }}
>
<div className='min-h-[calc(100vh-60px)] flex flex-row items-center justify-center '>
<div className={`max-w-md text-center bg-black p-8 m-8 bg-opacity-50 text-white backdrop-blur-sm rounded-xl movable-div ${isBoxVisible ? '' : 'move-out-left'}`}>
<h1 className="text-5xl font-bold">Utopia Game</h1>
<p className="py-6">ist mehr als nur ein Spiel. Es ist eine Bewegung, die darauf abzielt, die Spieler aus ihren virtuellen Welten zu befreien und sie zu inspirieren, das echte Leben zu erkunden, Fähigkeiten zu entwickeln und die Welt um sie herum zu gestalten. Bist du bereit, Teil dieser Revolution zu werden? </p>
<div className="btn !text-white btn-primary" onClick={startGame}>Play </div>
<div
className={`max-w-md text-center bg-black p-8 m-8 bg-opacity-50 text-white backdrop-blur-sm rounded-xl movable-div ${isBoxVisible ? '' : 'move-out-left'}`}
>
<h1 className='text-5xl font-bold'>Utopia Game</h1>
<p className='py-6'>
ist mehr als nur ein Spiel. Es ist eine Bewegung, die darauf abzielt, die Spieler
aus ihren virtuellen Welten zu befreien und sie zu inspirieren, das echte Leben zu
erkunden, Fähigkeiten zu entwickeln und die Welt um sie herum zu gestalten. Bist
du bereit, Teil dieser Revolution zu werden?{' '}
</p>
<div className='btn !text-white btn-primary' onClick={startGame}>
Play
</div>
</div>
<div className={`mockup-phone m-8 hidden lg:block movable-div ${isPhoneVisible ? '' : 'move-out-right'}`}>
<div className="camera"></div>
<div className="display">my-8
<div className="artboard artboard-demo phone-1">
<iframe src="/" height={568} width={320}></iframe>
<div
className={`mockup-phone m-8 hidden lg:block movable-div ${isPhoneVisible ? '' : 'move-out-right'}`}
>
<div className='camera'></div>
<div className='display'>
my-8
<div className='artboard artboard-demo phone-1'>
<iframe src='/' height={568} width={320}></iframe>
</div>
</div>
</div>
</div>
</div>
<section className="min-h-[50em] p-8 flex h-full items-center justify-center">
<ul className="my-8 grid gap-y-8 gap-x-12 sm:grid-cols-2 lg:grid-cols-3">
{
features?.map((item, idx) => (
<li key={idx} className="space-y-3">
<div className="w-12tw-card tw-card-body h-12 mx-auto !bg-transparent text-indigo-600 rounded-full flex items-center justify-center text-5xl">
{item.symbol}
</div>
<h4 className="text-lg font-semibold">
{item.heading}
</h4>
<p>
{item.text}
</p>
</li>
))
}
<section className='min-h-[50em] p-8 flex h-full items-center justify-center'>
<ul className='my-8 grid gap-y-8 gap-x-12 sm:grid-cols-2 lg:grid-cols-3'>
{features?.map((item, idx) => (
<li key={idx} className='space-y-3'>
<div className='w-12tw-card tw-card-body h-12 mx-auto !bg-transparent text-indigo-600 rounded-full flex items-center justify-center text-5xl'>
{item.symbol}
</div>
<h4 className='text-lg font-semibold'>{item.heading}</h4>
<p>{item.text}</p>
</li>
))}
</ul>
</section>
<section className="py-14 min-h-[40em] p-8 flex h-full items-center justify-center mb-28">
<div className="max-w-screen-xl mx-auto text-center">
<div className="max-w-xl mx-auto">
<h3 className="text-3xl font-semibold sm:text-4xl">
Meet our team
</h3>
<p className="mt-3">
Lorem Ipsum is simply dummy text of the printing and typesetting industry.Lorem Ipsum has been the industry's standard dummy.
<section className='py-14 min-h-[40em] p-8 flex h-full items-center justify-center mb-28'>
<div className='max-w-screen-xl mx-auto text-center'>
<div className='max-w-xl mx-auto'>
<h3 className='text-3xl font-semibold sm:text-4xl'>Meet our team</h3>
<p className='mt-3'>
Lorem Ipsum is simply dummy text of the printing and typesetting industry.Lorem
Ipsum has been the industry's standard dummy.
</p>
</div>
<div className="mt-12">
<ul className="grid gap-8 sm:grid-cols-2 md:grid-cols-3">
{
team?.map((item, idx) => (
<li key={idx}>
<div className="w-24 h-24 mx-auto">
<img
src={`https://api.utopia-lab.org/assets/${item.image}`}
className="w-full h-full rounded-full"
alt=""
/>
</div>
<div className="mt-2">
<h4 className="font-semibold sm:text-lg">{item.name}</h4>
<p className="text-indigo-600">{item.role}</p>
<p className="mt-2">{item.text}</p>
<div className="mt-4 flex justify-center gap-4">
</div>
</div>
</li>
))
}
<div className='mt-12'>
<ul className='grid gap-8 sm:grid-cols-2 md:grid-cols-3'>
{team?.map((item, idx) => (
<li key={idx}>
<div className='w-24 h-24 mx-auto'>
<img
src={`https://api.utopia-lab.org/assets/${item.image}`}
className='w-full h-full rounded-full'
alt=''
/>
</div>
<div className='mt-2'>
<h4 className='font-semibold sm:text-lg'>{item.name}</h4>
<p className='text-indigo-600'>{item.role}</p>
<p className='mt-2'>{item.text}</p>
<div className='mt-4 flex justify-center gap-4'></div>
</div>
</li>
))}
</ul>
</div>
</div>
</section>
</div>
</div>
<footer className="text-gray-500 bg-base-200 px-4 py-5 w-full mx-auto md:px-8 text-base">
<div className="mt-8 items-center justify-center flex">
<div className="mt-6 sm:mt-0">
<ul className="flex items-center space-x-4">
<li className="w-8 h-8 border-current bg-white rounded-full flex items-center justify-center">
<a href="https://t.me/UtopiaMap">
<svg stroke="currentColor" fill="#1d93d2" strokeWidth="0" viewBox="0 0 512 512" height="1.4rem" width="1.4rem" xmlns="http://www.w3.org/2000/svg"><path d="M446.7 98.6l-67.6 318.8c-5.1 22.5-18.4 28.1-37.3 17.5l-103-75.9-49.7 47.8c-5.5 5.5-10.1 10.1-20.7 10.1l7.4-104.9 190.9-172.5c8.3-7.4-1.8-11.5-12.9-4.1L117.8 284 16.2 252.2c-22.1-6.9-22.5-22.1 4.6-32.7L418.2 66.4c18.4-6.9 34.5 4.1 28.5 32.2z"></path></svg>
</a>
</li>
<li className="w-8 h-8 border-current bg-white rounded-full flex items-center justify-center">
<a href="mailto:hello@utopia-lab.org">
<svg stroke="currentColor" fill="#333" strokeWidth="0" viewBox="0 0 24 24" height="1.25rem" width="1.25rem" xmlns="http://www.w3.org/2000/svg"><path fill="none" d="M0 0h24v24H0z"></path><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4-8 5-8-5V6l8 5 8-5v2z"></path></svg> </a>
</li>
<li className="w-8 h-8 border-current bg-white rounded-full flex items-center justify-center">
<a href="https://twitter.com/UtopiaMapGame/" className='text-white'>
<svg className="svg-icon w-[1.4rem] h-[1.4rem] text-[#1d93d2]" viewBox="0 0 20 20">
<path fill="currentColor" d="M18.258,3.266c-0.693,0.405-1.46,0.698-2.277,0.857c-0.653-0.686-1.586-1.115-2.618-1.115c-1.98,0-3.586,1.581-3.586,3.53c0,0.276,0.031,0.545,0.092,0.805C6.888,7.195,4.245,5.79,2.476,3.654C2.167,4.176,1.99,4.781,1.99,5.429c0,1.224,0.633,2.305,1.596,2.938C2.999,8.349,2.445,8.19,1.961,7.925C1.96,7.94,1.96,7.954,1.96,7.97c0,1.71,1.237,3.138,2.877,3.462c-0.301,0.08-0.617,0.123-0.945,0.123c-0.23,0-0.456-0.021-0.674-0.062c0.456,1.402,1.781,2.422,3.35,2.451c-1.228,0.947-2.773,1.512-4.454,1.512c-0.291,0-0.575-0.016-0.855-0.049c1.588,1,3.473,1.586,5.498,1.586c6.598,0,10.205-5.379,10.205-10.045c0-0.153-0.003-0.305-0.01-0.456c0.7-0.499,1.308-1.12,1.789-1.827c-0.644,0.28-1.334,0.469-2.06,0.555C17.422,4.782,17.99,4.091,18.258,3.266"></path>
<footer className='text-gray-500 bg-base-200 px-4 py-5 w-full mx-auto md:px-8 text-base'>
<div className='mt-8 items-center justify-center flex'>
<div className='mt-6 sm:mt-0'>
<ul className='flex items-center space-x-4'>
<li className='w-8 h-8 border-current bg-white rounded-full flex items-center justify-center'>
<a href='https://t.me/UtopiaMap'>
<svg
stroke='currentColor'
fill='#1d93d2'
strokeWidth='0'
viewBox='0 0 512 512'
height='1.4rem'
width='1.4rem'
xmlns='http://www.w3.org/2000/svg'
>
<path d='M446.7 98.6l-67.6 318.8c-5.1 22.5-18.4 28.1-37.3 17.5l-103-75.9-49.7 47.8c-5.5 5.5-10.1 10.1-20.7 10.1l7.4-104.9 190.9-172.5c8.3-7.4-1.8-11.5-12.9-4.1L117.8 284 16.2 252.2c-22.1-6.9-22.5-22.1 4.6-32.7L418.2 66.4c18.4-6.9 34.5 4.1 28.5 32.2z'></path>
</svg>
</a>
</li>
<li className='w-8 h-8 border-current bg-white rounded-full flex items-center justify-center'>
<a href='mailto:hello@utopia-lab.org'>
<svg
stroke='currentColor'
fill='#333'
strokeWidth='0'
viewBox='0 0 24 24'
height='1.25rem'
width='1.25rem'
xmlns='http://www.w3.org/2000/svg'
>
<path fill='none' d='M0 0h24v24H0z'></path>
<path d='M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4-8 5-8-5V6l8 5 8-5v2z'></path>
</svg>{' '}
</a>
</li>
<li className='w-8 h-8 border-current bg-white rounded-full flex items-center justify-center'>
<a href='https://twitter.com/UtopiaMapGame/' className='text-white'>
<svg
className='svg-icon w-[1.4rem] h-[1.4rem] text-[#1d93d2]'
viewBox='0 0 20 20'
>
<path
fill='currentColor'
d='M18.258,3.266c-0.693,0.405-1.46,0.698-2.277,0.857c-0.653-0.686-1.586-1.115-2.618-1.115c-1.98,0-3.586,1.581-3.586,3.53c0,0.276,0.031,0.545,0.092,0.805C6.888,7.195,4.245,5.79,2.476,3.654C2.167,4.176,1.99,4.781,1.99,5.429c0,1.224,0.633,2.305,1.596,2.938C2.999,8.349,2.445,8.19,1.961,7.925C1.96,7.94,1.96,7.954,1.96,7.97c0,1.71,1.237,3.138,2.877,3.462c-0.301,0.08-0.617,0.123-0.945,0.123c-0.23,0-0.456-0.021-0.674-0.062c0.456,1.402,1.781,2.422,3.35,2.451c-1.228,0.947-2.773,1.512-4.454,1.512c-0.291,0-0.575-0.016-0.855-0.049c1.588,1,3.473,1.586,5.498,1.586c6.598,0,10.205-5.379,10.205-10.045c0-0.153-0.003-0.305-0.01-0.456c0.7-0.499,1.308-1.12,1.789-1.827c-0.644,0.28-1.334,0.469-2.06,0.555C17.422,4.782,17.99,4.091,18.258,3.266'
></path>
</svg>
</a>
</li>
</ul>
</div>
</div>
<div className="mt-8 flex item s-center justify-center">
&copy; 2024
</div>
<div className='mt-8 flex item s-center justify-center'>&copy; 2024</div>
</footer>
</MapOverlayPage>
)
}

View File

@ -1,104 +1,145 @@
import { UtopiaMap, Layer, ItemView, PopupButton, StartEndView, TextView, ItemForm, PopupStartEndInput, PopupTextAreaInput, PopupTextInput, LayerProps } from 'utopia-ui'
import { itemsApi } from '../api/itemsApi';
import { Place } from '../api/directus';
import { useEffect, useState } from 'react';
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable import/no-relative-parent-imports */
/* eslint-disable array-callback-return */
/* eslint-disable new-cap */
/* eslint-disable @typescript-eslint/no-empty-function */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable react-hooks/exhaustive-deps */
import { useEffect, useState } from 'react'
import {
UtopiaMap,
Layer,
ItemView,
PopupButton,
StartEndView,
TextView,
ItemForm,
PopupStartEndInput,
PopupTextAreaInput,
PopupTextInput,
} from 'utopia-ui'
type layerApi = {
id: string;
import { itemsApi } from '../api/itemsApi'
import type { Place } from '../api/directus'
import type { LayerProps } from 'utopia-ui'
interface layerApi {
id: string
api: itemsApi<Place>
}
function MapContainer({ layers, map }: { layers: Array<LayerProps>, map: any }) {
const [apis, setApis] = useState<Array<layerApi>>([]);
function MapContainer({ layers, map }: { layers: LayerProps[]; map: any }) {
const [apis, setApis] = useState<layerApi[]>([])
useEffect(() => {
// get timestamp for the end of the current day
let now = new Date();
let startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate());
let etartOfDayISO = startOfDay.toISOString();
const now = new Date()
const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate())
const etartOfDayISO = startOfDay.toISOString()
layers.map((layer: LayerProps) => {
apis && setApis(current => [...current, {
id: layer.id!, api: new itemsApi<Place>('items', layer.id, undefined, {
"_or": [
{
"end": {
"_gt": etartOfDayISO
}
},
{
"end": {
"_null": true
}
}
]
}
)
}])
apis &&
setApis((current) => [
...current,
{
id: layer.id!,
api: new itemsApi<Place>('items', layer.id, undefined, {
_or: [
{
end: {
_gt: etartOfDayISO,
},
},
{
end: {
_null: true,
},
},
],
}),
},
])
})
}, [layers])
useEffect(() => {
}, [apis])
useEffect(() => {}, [apis])
return (
<UtopiaMap
geo={map.geo}
zoom={map.zoom || 5}
center={map.center ? [map.center?.coordinates[1], map.center?.coordinates[0]] : [50.6, 9.5]}
height='100%'
width="100%"
width='100%'
showFilterControl={map.show_filter_control}
showLayerControl={map.show_layer_control}
showGratitudeControl={map.show_gratitude_control}
donationWidget = {map.donation_widget}
donationWidget={map.donation_widget}
>
{layers && apis &&
layers.map(layer =>
{layers &&
apis &&
layers.map((layer) => (
<Layer
id={layer.id}
key={layer.id}
name={layer.name}
menuIcon={"https://api.utopia-lab.org/assets/" + layer.menuIcon}
menuIcon={'https://api.utopia-lab.org/assets/' + layer.menuIcon}
menuText={layer.menuText}
menuColor={layer.menuColor}
markerIcon={layer.markerIcon}
markerShape={layer.markerShape}
userProfileLayer={layer.userProfileLayer}
markerDefaultColor={layer.menuColor}
markerDefaultColor2={layer.markerDefaultColor2 ? layer.markerDefaultColor2 : "RGBA(35, 31, 32, 0.2)"}
markerDefaultColor2={
layer.markerDefaultColor2 ? layer.markerDefaultColor2 : 'RGBA(35, 31, 32, 0.2)'
}
itemType={layer.itemType}
customEditLink='/edit-item'
customEditParameter='id'
public_edit_items={layer.public_edit_items}
listed={layer.listed}
api={apis?.find(api => api.id === layer.id)?.api}>
api={apis.find((api) => api.id === layer.id)?.api}
>
<ItemView>
{layer.itemType.show_start_end &&
<StartEndView></StartEndView>
}
{layer.itemType.show_profile_button &&
{layer.itemType.show_start_end && <StartEndView></StartEndView>}
{layer.itemType.show_profile_button && (
<PopupButton url={'/item'} parameterField={'id'} text={'Profile'} />
}
{layer.itemType.show_text &&
<TextView truncate></TextView>
}
)}
{layer.itemType.show_text && <TextView truncate></TextView>}
</ItemView>
<ItemForm>
{layer.itemType.show_name_input && <PopupTextInput dataField='name' placeholder='Name'></PopupTextInput>}
{layer.itemType.show_name_input && (
<PopupTextInput dataField='name' placeholder='Name'></PopupTextInput>
)}
{layer.itemType.show_start_end_input && <PopupStartEndInput></PopupStartEndInput>}
{layer.itemType.show_text_input && <div className='mt-4'><PopupTextAreaInput dataField='text' placeholder={'Text ...'} style="tw-h-40"></PopupTextAreaInput></div>}
{//layer.public_edit_items && <PopupCheckboxInput dataField={'public_edit'} label={'public edit'}/>
{layer.itemType.show_text_input && (
<div className='mt-4'>
<PopupTextAreaInput
dataField='text'
placeholder={'Text ...'}
style='tw-h-40'
></PopupTextAreaInput>
</div>
)}
{
// layer.public_edit_items && <PopupCheckboxInput dataField={'public_edit'} label={'public edit'}/>
}
{layer.itemType.custom_text && <div className='flex justify-center'>
<p>{layer.itemType.custom_text}</p>
</div>}
{layer.item_presets && Object.entries(layer.item_presets).map((ip: any) => <input key={ip[0]} type="hidden" id={ip[0]} name={ip[0]} value={ip[1]} />)}
{layer.itemType.custom_text && (
<div className='flex justify-center'>
<p>{layer.itemType.custom_text}</p>
</div>
)}
{layer.item_presets &&
Object.entries(layer.item_presets).map((ip: any) => (
<input key={ip[0]} type='hidden' id={ip[0]} name={ip[0]} value={ip[1]} />
))}
</ItemForm>
</Layer>)
}
</Layer>
))}
</UtopiaMap>
)
}

View File

@ -1,129 +1,115 @@
import { Item, Tag } from "utopia-ui";
import type { Item, Tag } from 'utopia-ui'
export const tags : Tag[] = [
{
"id": "423423423423",
"name": "Activism",
"color": "#6d398b"
},
{
"id": "4234423",
"name": "Art",
"color": "#fdc60b"
},
{
"id": "4231223423",
"name": "Community",
"color": "#FFA439"
},
{
"id": "429803423423",
"name": "Culture",
"color": "#f18e1c"
},
{
"id": "42423423",
"name": "Education",
"color": "#444e99"
},
{
"id": "4565654423",
"name": "Gardening",
"color": "#008e5b"
},
{
"id": "4234gfh423",
"name": "Healing",
"color": "#c4037d"
},
{
"id": "4223423",
"name": "Market",
"color": "#2a71b0"
},
{
"id": "42342gd3423",
"name": "Nature",
"color": "#8cbb26"
},
{
"id": "423123423",
"name": "Technology",
"color": "#0696bb"
}
export const tags: Tag[] = [
{
id: '423423423423',
name: 'Activism',
color: '#6d398b',
},
{
id: '4234423',
name: 'Art',
color: '#fdc60b',
},
{
id: '4231223423',
name: 'Community',
color: '#FFA439',
},
{
id: '429803423423',
name: 'Culture',
color: '#f18e1c',
},
{
id: '42423423',
name: 'Education',
color: '#444e99',
},
{
id: '4565654423',
name: 'Gardening',
color: '#008e5b',
},
{
id: '4234gfh423',
name: 'Healing',
color: '#c4037d',
},
{
id: '4223423',
name: 'Market',
color: '#2a71b0',
},
{
id: '42342gd3423',
name: 'Nature',
color: '#8cbb26',
},
{
id: '423123423',
name: 'Technology',
color: '#0696bb',
},
]
export const events : Item[] = [
export const events: Item[] = [
{
"id": "243253f3645643",
"name": "Vollmondtrommeln",
"text": "Zu den Vollmonden vom März bis Oktober treffen sich traditionell Menschen zum gemeinsamen Musizieren, Tanzen, Spielen, Grillen und Entspannen am Gerloser Häuschen im Niesiger Wald.\r\n\r\nUhrzeit: immer ab 17 Uhr\r\n\r\nhttps://trommeln-fulda.de/vollmondtrommeln/",
"position": {
"type": "Point",
"coordinates": [
9.667615,
50.588632
]
},
"start": "2022-03-18T12:00:00",
"end": "2022-10-08T12:00:00",
}, {
"id": "fsdfsdfsdfsdfdse",
"name": "anderes Event",
"text": "Zu den Vollmonden vom März bis Oktober treffen sich traditionell Menschen zum gemeinsamen Musizieren, Tanzen, Spielen, Grillen und Entspannen am Gerloser Häuschen im Niesiger Wald.\r\n\r\nUhrzeit: immer ab 17 Uhr\r\n\r\nhttps://trommeln-fulda.de/vollmondtrommeln/",
"position": {
"type": "Point",
"coordinates": [
9.68,
50.59
]
id: '243253f3645643',
name: 'Vollmondtrommeln',
text: 'Zu den Vollmonden vom März bis Oktober treffen sich traditionell Menschen zum gemeinsamen Musizieren, Tanzen, Spielen, Grillen und Entspannen am Gerloser Häuschen im Niesiger Wald.\r\n\r\nUhrzeit: immer ab 17 Uhr\r\n\r\nhttps://trommeln-fulda.de/vollmondtrommeln/',
position: {
type: 'Point',
coordinates: [9.667615, 50.588632],
},
"start": "2022-03-18T12:00:00",
"end": "2022-10-08T12:00:00",
}
];
export const places : Item[] = [
{
"id": "1asdasdasd",
"name": "Gärtnerei am Leisebach",
"text": "Wir sind eine Bio-Gemüsegärtnerei und suchen noch fleißige Helfer für diese Saison.",
"position": {
"type": "Point",
"coordinates": [
8.476371,
51.0044
]
},
"date_created": "2021-04-15T07:46:26.906Z",
"date_updated": "2021-04-15T07:46:26.906Z"
start: '2022-03-18T12:00:00',
end: '2022-10-08T12:00:00',
},
{
"id": "adsasdas2",
"name": "Rainbow Crystal Land",
"text": "https://rainbowcrystal.land",
"position": {
"type": "Point",
"coordinates": [
-76.367426,
1.87
]
id: 'fsdfsdfsdfsdfdse',
name: 'anderes Event',
text: 'Zu den Vollmonden vom März bis Oktober treffen sich traditionell Menschen zum gemeinsamen Musizieren, Tanzen, Spielen, Grillen und Entspannen am Gerloser Häuschen im Niesiger Wald.\r\n\r\nUhrzeit: immer ab 17 Uhr\r\n\r\nhttps://trommeln-fulda.de/vollmondtrommeln/',
position: {
type: 'Point',
coordinates: [9.68, 50.59],
},
"date_created": "2021-04-04T18:01:24.596Z",
"date_updated": "2021-04-04T18:01:24.596Z"
start: '2022-03-18T12:00:00',
end: '2022-10-08T12:00:00',
},
]
export const places: Item[] = [
{
id: '1asdasdasd',
name: 'Gärtnerei am Leisebach',
text: 'Wir sind eine Bio-Gemüsegärtnerei und suchen noch fleißige Helfer für diese Saison.',
position: {
type: 'Point',
coordinates: [8.476371, 51.0044],
},
date_created: '2021-04-15T07:46:26.906Z',
date_updated: '2021-04-15T07:46:26.906Z',
},
{
"id": "f345v34",
"name": "🌈 RainbowCrystaleARThshipKinderGarten",
"text": "AMYTIME WELCOME HOME\r\n\r\n#lebenliebenlernen\r\n#symbioticsynergysolutions\r\n#lebenlanglachen\r\n#soriendosiempresaludpazyamor\r\n#laughinglearninglivingloving\r\n#souriresantétoujoursamoure\r\n\r\n\r\n<b>Garden IntroductionVideo</b>\r\n<br>\r\n\r\n\r\n<iframe width='250' src='https://www.youtube-nocookie.com/embed/7jUaixJGK08' title='YouTube video player' frameborder='0' allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture' allowfullscreen></iframe>",
"position": {
"type": "Point",
"coordinates": [
9.502648,
51.334741
]
id: 'adsasdas2',
name: 'Rainbow Crystal Land',
text: 'https://rainbowcrystal.land',
position: {
type: 'Point',
coordinates: [-76.367426, 1.87],
},
"date_created": "2022-03-13T23:09:56.305Z",
"date_updated": "2022-03-13T23:09:56.305Z"
}]
date_created: '2021-04-04T18:01:24.596Z',
date_updated: '2021-04-04T18:01:24.596Z',
},
{
id: 'f345v34',
name: '🌈 RainbowCrystaleARThshipKinderGarten',
text: "AMYTIME WELCOME HOME\r\n\r\n#lebenliebenlernen\r\n#symbioticsynergysolutions\r\n#lebenlanglachen\r\n#soriendosiempresaludpazyamor\r\n#laughinglearninglivingloving\r\n#souriresantétoujoursamoure\r\n\r\n\r\n<b>Garden IntroductionVideo</b>\r\n<br>\r\n\r\n\r\n<iframe width='250' src='https://www.youtube-nocookie.com/embed/7jUaixJGK08' title='YouTube video player' frameborder='0' allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture' allowfullscreen></iframe>",
position: {
type: 'Point',
coordinates: [9.502648, 51.334741],
},
date_created: '2022-03-13T23:09:56.305Z',
date_updated: '2022-03-13T23:09:56.305Z',
},
]

View File

@ -1,22 +1,17 @@
import { MapIcon } from '@heroicons/react/24/outline'
export const routes = [
{
path: '/',
icon: <MapIcon style={{width: 24 }}/>,
icon: <MapIcon style={{ width: 24 }} />,
name: 'Map',
}/**
} /**
{
path: '/people', // url
icon: <UsersIcon style={{width: 24 }}/>, // icon component
name: 'People', // name that appear in Sidebar
}, */,
/*
{
path: '', //no url needed as this has submenu
@ -44,21 +39,29 @@ export const routes = [
]
export const getBottomRoutes = (currentUrl: string) => {
const url = new URL(currentUrl);
const isEmbedded = url.searchParams.get('embedded') === 'true';
const url = new URL(currentUrl)
const isEmbedded = url.searchParams.get('embedded') === 'true'
const bottomRoutes = [
// Other routes can be added here
];
]
if (!isEmbedded) {
bottomRoutes.push({
path: 'https://github.com/utopia-os/utopia-ui', // url
icon: <svg viewBox="0 0 24 24" aria-hidden="true" className="tw-h-6 tw-w-6" stroke="currentColor"><path fillRule="evenodd" clipRule="evenodd" d="M12 2C6.477 2 2 6.463 2 11.97c0 4.404 2.865 8.14 6.839 9.458.5.092.682-.216.682-.48 0-.236-.008-.864-.013-1.695-2.782.602-3.369-1.337-3.369-1.337-.454-1.151-1.11-1.458-1.11-1.458-.908-.618.069-.606.069-.606 1.003.07 1.531 1.027 1.531 1.027.892 1.524 2.341 1.084 2.91.828.092-.643.35-1.083.636-1.332-2.22-.251-4.555-1.107-4.555-4.927 0-1.088.39-1.979 1.029-2.675-.103-.252-.446-1.266.098-2.638 0 0 .84-.268 2.75 1.022A9.607 9.607 0 0 1 12 6.82c.85.004 1.705.114 2.504.336 1.909-1.29 2.747-1.022 2.747-1.022.546 1.372.202 2.386.1 2.638.64.696 1.028 1.587 1.028 2.675 0 3.83-2.339 4.673-4.566 4.92.359.307.678.915.678 1.846 0 1.332-.012 2.407-.012 2.734 0 .267.18.577.688.48 3.97-1.32 6.833-5.054 6.833-9.458C22 6.463 17.522 2 12 2Z"></path></svg>,
icon: (
<svg viewBox='0 0 24 24' aria-hidden='true' className='tw-h-6 tw-w-6' stroke='currentColor'>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M12 2C6.477 2 2 6.463 2 11.97c0 4.404 2.865 8.14 6.839 9.458.5.092.682-.216.682-.48 0-.236-.008-.864-.013-1.695-2.782.602-3.369-1.337-3.369-1.337-.454-1.151-1.11-1.458-1.11-1.458-.908-.618.069-.606.069-.606 1.003.07 1.531 1.027 1.531 1.027.892 1.524 2.341 1.084 2.91.828.092-.643.35-1.083.636-1.332-2.22-.251-4.555-1.107-4.555-4.927 0-1.088.39-1.979 1.029-2.675-.103-.252-.446-1.266.098-2.638 0 0 .84-.268 2.75 1.022A9.607 9.607 0 0 1 12 6.82c.85.004 1.705.114 2.504.336 1.909-1.29 2.747-1.022 2.747-1.022.546 1.372.202 2.386.1 2.638.64.696 1.028 1.587 1.028 2.675 0 3.83-2.339 4.673-4.566 4.92.359.307.678.915.678 1.846 0 1.332-.012 2.407-.012 2.734 0 .267.18.577.688.48 3.97-1.32 6.833-5.054 6.833-9.458C22 6.463 17.522 2 12 2Z'
></path>
</svg>
),
name: 'Github', // name that appear in Sidebar
blank: true
});
blank: true,
})
}
return bottomRoutes;
};
return bottomRoutes
}

View File

@ -1,6 +1,6 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ["./src/**/*.{html,js,jsx,tsx,ts}"],
content: ['./src/**/*.{html,js,jsx,tsx,ts}'],
theme: {
extend: {
// that is animation class
@ -9,9 +9,8 @@ export default {
},
},
},
plugins: [import("daisyui")],
plugins: [import('daisyui')],
daisyui: {
themes: ["light", "dark", "cupcake", "retro", "cyberpunk", "aqua"]
}
themes: ['light', 'dark', 'cupcake', 'retro', 'cyberpunk', 'aqua'],
},
}