fix(source): update tailwind and daisyui (#196)

* removed daisy from config

* removed tw-elements artefact

* removed comments from tailwind config

* removed safelist

* migrated to tailwind4 and daisyui5

* deleted tailwind.config.js which is not eeded anymore

* 3.0.79

* version number

* fixed broken layouts

* more fixing

* more layout fixing

* tested theming

* small fixes

* adapt snapshots to changes

* package.json: add unit test update script

* more ui refactoring & theme controller

* ui improvements

* package-lock.json

* fix linting

* fixed tabs

* fix linting

* fixed typing

---------

Co-authored-by: mahula <lenzmath@posteo.de>
This commit is contained in:
Anton Tranelis 2025-04-25 15:03:42 +01:00 committed by GitHub
parent 67a5e6e22d
commit 9e6bcf1846
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
104 changed files with 1716 additions and 1651 deletions

1332
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -39,6 +39,7 @@
"@rollup/plugin-alias": "^5.1.1", "@rollup/plugin-alias": "^5.1.1",
"@rollup/plugin-node-resolve": "^16.0.0", "@rollup/plugin-node-resolve": "^16.0.0",
"@rollup/plugin-typescript": "^12.1.2", "@rollup/plugin-typescript": "^12.1.2",
"@tailwindcss/postcss": "^4.0.14",
"@testing-library/jest-dom": "^6.6.3", "@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0", "@testing-library/react": "^16.2.0",
"@types/geojson": "^7946.0.14", "@types/geojson": "^7946.0.14",
@ -50,9 +51,8 @@
"@typescript-eslint/parser": "^5.62.0", "@typescript-eslint/parser": "^5.62.0",
"@vitejs/plugin-react": "^4.3.4", "@vitejs/plugin-react": "^4.3.4",
"@vitest/coverage-v8": "^3.0.5", "@vitest/coverage-v8": "^3.0.5",
"autoprefixer": "^10.4.14",
"cypress": "^14.0.3", "cypress": "^14.0.3",
"daisyui": "^4.6.1", "daisyui": "^5.0.6",
"eslint": "^8.24.0", "eslint": "^8.24.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-config-standard": "^17.1.0", "eslint-config-standard": "^17.1.0",
@ -77,7 +77,7 @@
"rollup-plugin-dts": "^6.1.1", "rollup-plugin-dts": "^6.1.1",
"rollup-plugin-postcss": "^4.0.2", "rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-svg": "^2.0.0", "rollup-plugin-svg": "^2.0.0",
"tailwindcss": "^3.3.1", "tailwindcss": "^4.0.14",
"typedoc": "^0.27.6", "typedoc": "^0.27.6",
"typedoc-plugin-coverage": "^3.4.1", "typedoc-plugin-coverage": "^3.4.1",
"typedoc-plugin-missing-exports": "^3.1.0", "typedoc-plugin-missing-exports": "^3.1.0",

View File

@ -1,7 +1,6 @@
// eslint-disable-next-line import/no-commonjs // eslint-disable-next-line import/no-commonjs
module.exports = { module.exports = {
plugins: { plugins: {
tailwindcss: {}, '@tailwindcss/postcss': {},
autoprefixer: {},
}, },
} }

View File

@ -55,11 +55,9 @@ export default [
'react-toastify', 'react-toastify',
'react-string-replace', 'react-string-replace',
'react-toastify/dist/ReactToastify.css', 'react-toastify/dist/ReactToastify.css',
'tw-elements',
'react-router-dom', 'react-router-dom',
'react-leaflet-cluster', 'react-leaflet-cluster',
'@tanstack/react-query', '@tanstack/react-query',
'tributejs',
'prop-types', 'prop-types',
'leaflet/dist/leaflet.css', 'leaflet/dist/leaflet.css',
'@heroicons/react/20/solid', '@heroicons/react/20/solid',

View File

@ -24,14 +24,14 @@ export function AppShell({
}) { }) {
return ( return (
<ContextWrapper> <ContextWrapper>
<div className='tw-flex tw-flex-col tw-h-full'> <div className='tw:flex tw:flex-col tw:h-full'>
<SetAppState <SetAppState
assetsApi={assetsApi} assetsApi={assetsApi}
embedded={embedded} embedded={embedded}
openCollectiveApiKey={openCollectiveApiKey} openCollectiveApiKey={openCollectiveApiKey}
/> />
<NavBar appName={appName}></NavBar> <NavBar appName={appName}></NavBar>
<div id='app-content' className='tw-flex'> <div id='app-content' className='tw:flex'>
{children} {children}
</div> </div>
</div> </div>

View File

@ -12,7 +12,7 @@ export function Content({ children }: ContentProps) {
return ( return (
<div <div
className={`${appState.sideBarOpen && !appState.sideBarSlim ? 'tw-ml-48' : appState.sideBarOpen && appState.sideBarSlim ? 'tw-ml-14' : ''} tw-w-full tw-h-full tw-bg-base-100 tw-relative tw-transition-all tw-duration-300`} className={`${appState.sideBarOpen && !appState.sideBarSlim ? 'tw:ml-48' : appState.sideBarOpen && appState.sideBarSlim ? 'tw:ml-14' : ''} tw:w-full tw:h-full tw:bg-base-200 tw:relative tw:transition-all tw:duration-300`}
> >
{children} {children}
</div> </div>

View File

@ -22,7 +22,7 @@ const ContextCheckContext = createContext(false)
const CloseButton = ({ closeToast }: CloseButtonProps) => ( const CloseButton = ({ closeToast }: CloseButtonProps) => (
<button <button
className='tw-btn tw-btn-sm tw-btn-circle tw-btn-ghost tw-absolute tw-right-2 tw-top-2 focus:tw-outline-none' className='tw:btn tw:btn-sm tw:btn-circle tw:btn-ghost tw:absolute tw:right-2 tw:top-2 tw:focus:outline-hidden'
onClick={closeToast} onClick={closeToast}
> >

View File

@ -1,23 +1,14 @@
import Bars3Icon from '@heroicons/react/16/solid/Bars3Icon' import Bars3Icon from '@heroicons/react/16/solid/Bars3Icon'
import EllipsisVerticalIcon from '@heroicons/react/16/solid/EllipsisVerticalIcon'
import QuestionMarkIcon from '@heroicons/react/24/outline/QuestionMarkCircleIcon' import QuestionMarkIcon from '@heroicons/react/24/outline/QuestionMarkCircleIcon'
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { toast } from 'react-toastify'
import { useAuth } from '#components/Auth/useAuth' import { ThemeControl } from '#components/Templates/ThemeControl'
import { useItems } from '#components/Map/hooks/useItems'
import { useAppState, useSetAppState } from './hooks/useAppState' import { useAppState, useSetAppState } from './hooks/useAppState'
import { UserControl } from './UserControl'
import type { Item } from '#types/Item'
export default function NavBar({ appName }: { appName: string }) { export default function NavBar({ appName }: { appName: string }) {
const { isAuthenticated, user, logout } = useAuth()
const [userProfile, setUserProfile] = useState<Item>({} as Item)
const items = useItems()
const appState = useAppState() const appState = useAppState()
const setAppState = useSetAppState() const setAppState = useSetAppState()
@ -25,14 +16,6 @@ export default function NavBar({ appName }: { appName: string }) {
setAppState({ sideBarOpen: !appState.sideBarOpen }) setAppState({ sideBarOpen: !appState.sideBarOpen })
} }
useEffect(() => {
const profile =
user && items.find((i) => i.user_created?.id === user.id && i.layer?.userProfileLayer)
profile
? setUserProfile(profile)
: setUserProfile({ id: crypto.randomUUID(), name: user?.first_name ?? '', text: '' })
}, [user, items])
const nameRef = useRef<HTMLHeadingElement>(null) const nameRef = useRef<HTMLHeadingElement>(null)
const [nameWidth, setNameWidth] = useState<number>(0) const [nameWidth, setNameWidth] = useState<number>(0)
@ -40,128 +23,42 @@ export default function NavBar({ appName }: { appName: string }) {
!appState.embedded && nameRef.current && setNameWidth(nameRef.current.scrollWidth) !appState.embedded && nameRef.current && setNameWidth(nameRef.current.scrollWidth)
}, [nameRef, appName, appState.embedded]) }, [nameRef, appName, appState.embedded])
const onLogout = async () => {
await toast.promise(logout(), {
success: {
render() {
return 'Bye bye'
},
// other options
icon: '👋',
},
error: {
render({ data }) {
return JSON.stringify(data)
},
},
pending: 'logging out ..',
})
}
if (!appState.embedded) { if (!appState.embedded) {
return ( return (
<> <>
<div className='tw-navbar tw-bg-base-100 tw-z-[9998] tw-shadow-xl tw-relative'> <div className='tw:navbar tw:bg-base-100 tw:z-9998 tw:shadow-xl tw:relative tw:p-0'>
<button <button
className='tw-btn tw-btn-square tw-btn-ghost' className='tw:btn tw:btn-square tw:btn-ghost tw:ml-3'
aria-controls='#sidenav' aria-controls='#sidenav'
aria-haspopup='true' aria-haspopup='true'
onClick={() => toggleSidebar()} onClick={() => toggleSidebar()}
> >
<Bars3Icon className='tw-inline-block tw-w-5 tw-h-5' /> <Bars3Icon className='tw:inline-block tw:w-5 tw:h-5' />
</button> </button>
<div className='tw-flex-1 tw-mr-2'> <div className='tw:flex-1 tw:mr-2'>
<div <div
className={'tw-flex-1 tw-truncate tw-grid tw-grid-flow-col'} className={'tw:flex-1 tw:truncate tw:grid tw:grid-flow-col'}
style={{ maxWidth: nameWidth + 60 }} style={{ maxWidth: nameWidth + 60 }}
> >
<Link <Link
className='tw-btn tw-btn-ghost tw-px-2 tw-normal-case tw-text-xl tw-flex-1 tw-truncate' className='tw:btn tw:btn-ghost tw:px-2 tw:normal-case tw:text-xl tw:flex-1 tw:truncate'
to={'/'} to={'/'}
> >
<h1 ref={nameRef} className='tw-truncate'> <h1 ref={nameRef} className='tw:truncate'>
{appName} {appName}
</h1> </h1>
</Link> </Link>
<button <button
className='tw-btn tw-px-2 tw-btn-ghost' className='tw:btn tw:px-2 tw:btn-ghost'
onClick={() => window.my_modal_3.showModal()} onClick={() => window.my_modal_3.showModal()}
> >
<QuestionMarkIcon className='tw-h-5 tw-w-5' /> <QuestionMarkIcon className='tw:h-5 tw:w-5' />
</button> </button>
</div> </div>
</div> </div>
{isAuthenticated ? ( {appState.showThemeControl && <ThemeControl />}
<div className='tw-flex-none'> <UserControl />
<Link
to={`${userProfile.id && '/item/' + userProfile.id}`}
className='tw-flex tw-items-center'
>
{userProfile.image && (
<div className='tw-avatar'>
<div className='tw-w-10 tw-rounded-full'>
<img src={appState.assetsApi.url + userProfile.image} />
</div>
</div>
)}
<div className='tw-ml-2 tw-mr-2'>{userProfile.name || user?.first_name}</div>
</Link>
<div className='tw-dropdown tw-dropdown-end'>
<label tabIndex={0} className='tw-btn tw-btn-ghost tw-btn-square'>
<EllipsisVerticalIcon className='tw-h-5 tw-w-5' />
</label>
<ul
tabIndex={0}
className='tw-menu tw-menu-compact tw-dropdown-content tw-mt-3 tw-p-2 tw-shadow tw-bg-base-100 tw-rounded-box tw-w-52 !tw-z-[10000]'
>
<li>
<Link to={`${userProfile.id && '/edit-item/' + userProfile.id}`}>Profile</Link>
</li>
<li>
<Link to={'/user-settings'}>Settings</Link>
</li>
<li>
<a
onClick={() => {
void onLogout()
}}
>
Logout
</a>
</li>
</ul>
</div>
</div>
) : (
<div>
<div className='tw-hidden md:tw-flex'>
<Link to={'/login'}>
<div className='tw-btn tw-btn-ghost tw-mr-2'>Login</div>
</Link>
<Link to={'/signup'}>
<div className='tw-btn tw-btn-ghost tw-mr-2'>Sign Up</div>
</Link>
</div>
<div className='tw-dropdown tw-dropdown-end'>
<label tabIndex={1} className='tw-btn tw-btn-ghost md:tw-hidden'>
<EllipsisVerticalIcon className='tw-h-5 tw-w-5' />
</label>
<ul
tabIndex={1}
className='tw-menu tw-dropdown-content tw-mt-3 tw-p-2 tw-shadow tw-bg-base-100 tw-rounded-box tw-w-52 !tw-z-[10000]'
>
<li>
<Link to={'/login'}>Login</Link>
</li>
<li>
<Link to={'/signup'}>Sign Up</Link>
</li>
</ul>
</div>
</div>
)}
</div> </div>
</> </>
) )

View File

@ -34,19 +34,16 @@ export function SideBar({ routes, bottomRoutes }: { routes: Route[]; bottomRoute
return ( return (
<nav <nav
id='sidenav' id='sidenav'
className={`${appState.sideBarOpen ? 'tw-translate-x-0' : '-tw-translate-x-full'} className={`${appState.sideBarOpen ? 'tw:translate-x-0' : 'tw:-translate-x-full'}
${appState.sideBarSlim ? 'tw-w-14' : 'tw-w-48'} ${appState.sideBarSlim ? 'tw:w-14' : 'tw:w-48'}
${appState.embedded ? 'tw-mt-0 tw-h-[100dvh]' : 'tw-mt-16 tw-h-[calc(100dvh-64px)]'} ${appState.embedded ? 'tw:mt-5.5 tw:h-[calc(100dvh-22px)]' : 'tw:mt-16 tw:h-[calc(100dvh-64px)]'}
tw-fixed tw-left-0 tw-transition-all tw-duration-300 tw-top-0 tw-z-[10035] tw:fixed tw:left-0 tw:transition-all tw:duration-300 tw:top-0 tw:z-10035
tw-overflow-hidden tw-shadow-xl dark:tw-bg-zinc-800`} tw:overflow-hidden tw:shadow-xl tw:dark:bg-zinc-800`}
> >
<div <div
className={`tw-flex tw-flex-col ${appState.embedded ? 'tw-h-full' : 'tw-h-[calc(100dvh-64px)]'}`} className={`tw:flex tw:flex-col ${appState.embedded ? 'tw:h-full' : 'tw:h-[calc(100dvh-64px)]'}`}
> >
<ul <ul className='tw:menu tw:w-full tw:bg-base-100 tw:text-base-content tw:p-0'>
className='tw-menu tw-w-full tw-bg-base-100 tw-text-base-content tw-p-0'
data-te-sidenav-menu-ref
>
{routes.map((route, k) => { {routes.map((route, k) => {
return ( return (
<li className='' key={k}> <li className='' key={k}>
@ -59,7 +56,7 @@ export function SideBar({ routes, bottomRoutes }: { routes: Route[]; bottomRoute
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
to={`${route.path}${params && '?' + params.toString()}`} to={`${route.path}${params && '?' + params.toString()}`}
className={({ isActive }) => className={({ isActive }) =>
`${isActive ? 'tw-font-semibold tw-bg-base-200 !tw-rounded-none' : 'tw-font-normal !tw-rounded-none'}` `${isActive ? 'tw:font-semibold tw:bg-base-200 tw:rounded-none!' : 'tw:font-normal tw:rounded-none!'}`
} }
onClick={() => { onClick={() => {
if (screen.width < 640 && !appState.sideBarSlim) toggleSidebarOpen() if (screen.width < 640 && !appState.sideBarSlim) toggleSidebarOpen()
@ -67,7 +64,7 @@ export function SideBar({ routes, bottomRoutes }: { routes: Route[]; bottomRoute
> >
{route.icon} {route.icon}
<span <span
className={`${appState.sideBarSlim ? 'tw-hidden' : ''}`} className={`${appState.sideBarSlim ? 'tw:hidden' : ''}`}
data-te-sidenav-slim='false' data-te-sidenav-slim='false'
> >
{route.name} {route.name}
@ -75,7 +72,7 @@ export function SideBar({ routes, bottomRoutes }: { routes: Route[]; bottomRoute
{(location.pathname.includes(route.path) && route.path.length > 1) || {(location.pathname.includes(route.path) && route.path.length > 1) ||
location.pathname === route.path ? ( location.pathname === route.path ? (
<span <span
className='tw-absolute tw-inset-y-0 tw-left-0 tw-w-1 tw-rounded-tr-md tw-rounded-br-md tw-bg-primary ' className='tw:absolute tw:inset-y-0 tw:left-0 tw:w-1 tw:rounded-tr-md tw:rounded-br-md tw:bg-primary '
aria-hidden='true' aria-hidden='true'
></span> ></span>
) : null} ) : null}
@ -88,12 +85,12 @@ export function SideBar({ routes, bottomRoutes }: { routes: Route[]; bottomRoute
<div <div
id='slim-toggler' id='slim-toggler'
className='tw-w-full tw-bg-base-100 tw-flex-1 tw-grid tw-place-items-end' className='tw:w-full tw:bg-base-100 tw:flex-1 tw:grid tw:place-items-end'
aria-haspopup='true' aria-haspopup='true'
> >
<div className='tw-w-full'> <div className='tw:w-full'>
<ul <ul
className='tw-menu tw-w-full tw-bg-base-100 tw-text-base-content tw-p-0 tw-mb-0' className='tw:menu tw:w-full tw:bg-base-100 tw:text-base-content tw:p-0 tw:mb-0'
data-te-sidenav-menu-ref data-te-sidenav-menu-ref
> >
{bottomRoutes?.map((route, k) => { {bottomRoutes?.map((route, k) => {
@ -107,7 +104,7 @@ export function SideBar({ routes, bottomRoutes }: { routes: Route[]; bottomRoute
target={route.blank ? '_blank' : '_self'} target={route.blank ? '_blank' : '_self'}
to={route.path} to={route.path}
className={({ isActive }) => className={({ isActive }) =>
`${isActive ? 'tw-font-semibold tw-bg-base-200 !tw-rounded-none' : 'tw-font-normal !tw-rounded-none'}` `${isActive ? 'tw:font-semibold tw:bg-base-200 tw:rounded-none!' : 'tw:font-normal tw:rounded-none!'}`
} }
onClick={() => { onClick={() => {
if (screen.width < 640 && !appState.sideBarSlim) toggleSidebarOpen() if (screen.width < 640 && !appState.sideBarSlim) toggleSidebarOpen()
@ -115,7 +112,7 @@ export function SideBar({ routes, bottomRoutes }: { routes: Route[]; bottomRoute
> >
{route.icon} {route.icon}
<span <span
className={`${appState.sideBarSlim ? 'tw-hidden' : ''}`} className={`${appState.sideBarSlim ? 'tw:hidden' : ''}`}
data-te-sidenav-slim='false' data-te-sidenav-slim='false'
> >
{route.name} {route.name}
@ -123,7 +120,7 @@ export function SideBar({ routes, bottomRoutes }: { routes: Route[]; bottomRoute
{(location.pathname.includes(route.path) && route.path.length > 1) || {(location.pathname.includes(route.path) && route.path.length > 1) ||
location.pathname === route.path ? ( location.pathname === route.path ? (
<span <span
className='tw-absolute tw-inset-y-0 tw-left-0 tw-w-1 tw-rounded-tr-md tw-rounded-br-md tw-bg-primary ' className='tw:absolute tw:inset-y-0 tw:left-0 tw:w-1 tw:rounded-tr-md tw:rounded-br-md tw:bg-primary '
aria-hidden='true' aria-hidden='true'
></span> ></span>
) : null} ) : null}
@ -136,8 +133,8 @@ export function SideBar({ routes, bottomRoutes }: { routes: Route[]; bottomRoute
<ChevronRightIcon <ChevronRightIcon
className={ className={
'tw-w-5 tw-h-5 tw-mb-4 tw-mr-4 tw-cursor-pointer tw-float-right tw-delay-400 tw-duration-500 tw-transition-all ' + 'tw:w-5 tw:h-5 tw:mb-4 tw:mr-5 tw:mt-2 tw:cursor-pointer tw:float-right tw:delay-400 tw:duration-500 tw:transition-all ' +
(!appState.sideBarSlim ? 'tw-rotate-180' : '') (!appState.sideBarSlim ? 'tw:rotate-180' : '')
} }
onClick={() => toggleSidebarSlim()} onClick={() => toggleSidebarSlim()}
/> />

View File

@ -0,0 +1,119 @@
import EllipsisVerticalIcon from '@heroicons/react/16/solid/EllipsisVerticalIcon'
import { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import { toast } from 'react-toastify'
import { useAuth } from '#components/Auth/useAuth'
import { useItems } from '#components/Map/hooks/useItems'
import { useAppState } from './hooks/useAppState'
import type { Item } from '#types/Item'
export const UserControl = () => {
const { isAuthenticated, user, logout } = useAuth()
const appState = useAppState()
const [userProfile, setUserProfile] = useState<Item>({} as Item)
const items = useItems()
useEffect(() => {
const profile =
user && items.find((i) => i.user_created?.id === user.id && i.layer?.userProfileLayer)
profile
? setUserProfile(profile)
: setUserProfile({ id: crypto.randomUUID(), name: user?.first_name ?? '', text: '' })
}, [user, items])
const onLogout = async () => {
await toast.promise(logout(), {
success: {
render() {
return 'Bye bye'
},
// other options
icon: '👋',
},
error: {
render({ data }) {
return JSON.stringify(data)
},
},
pending: 'logging out ..',
})
}
return (
<>
{isAuthenticated ? (
<div className='tw:flex tw:mr-2'>
<Link
to={`${userProfile.id && '/item/' + userProfile.id}`}
className='tw:flex tw:items-center'
>
{userProfile.image && (
<div className='tw:avatar'>
<div className='tw:w-10 tw:rounded-full'>
<img src={appState.assetsApi.url + userProfile.image} />
</div>
</div>
)}
<div className='tw:ml-2 tw:mr-2'>{userProfile.name || user?.first_name}</div>
</Link>
<div className='tw:dropdown tw:dropdown-end'>
<label tabIndex={0} className='tw:btn tw:btn-ghost tw:btn-square'>
<EllipsisVerticalIcon className='tw:h-5 tw:w-5' />
</label>
<ul
tabIndex={0}
className='tw:menu tw:menu-compact tw:dropdown-content tw:mt-4 tw:p-2 tw:shadow tw:bg-base-100 tw:rounded-box tw:w-52 tw:z-10000!'
>
<li>
<Link to={`${userProfile.id && '/edit-item/' + userProfile.id}`}>Profile</Link>
</li>
<li>
<Link to={'/user-settings'}>Settings</Link>
</li>
<li>
<a
onClick={() => {
void onLogout()
}}
>
Logout
</a>
</li>
</ul>
</div>
</div>
) : (
<div className='tw:mr-2 tw:flex tw:items-center'>
<div className='tw:hidden tw:md:flex'>
<Link to={'/login'}>
<div className='tw:self-center tw:btn tw:btn-ghost tw:mr-2'>Login</div>
</Link>
<Link to={'/signup'}>
<div className='tw:btn tw:btn-ghost tw:mr-2'>Sign Up</div>
</Link>
</div>
<div className='tw:dropdown tw:dropdown-end'>
<label tabIndex={1} className='tw:btn tw:btn-ghost tw:md:hidden'>
<EllipsisVerticalIcon className='tw:h-5 tw:w-5' />
</label>
<ul
tabIndex={1}
className='tw:menu tw:dropdown-content tw:mt-4 tw:p-2 tw:shadow tw:bg-base-100 tw:rounded-box tw:w-52 tw:z-10000!'
>
<li>
<Link to={'/login'}>Login</Link>
</li>
<li>
<Link to={'/signup'}>Sign Up</Link>
</li>
</ul>
</div>
</div>
)}
</>
)
}

View File

@ -8,6 +8,7 @@ interface AppState {
assetsApi: AssetsApi assetsApi: AssetsApi
sideBarOpen: boolean sideBarOpen: boolean
sideBarSlim: boolean sideBarSlim: boolean
showThemeControl: boolean
embedded: boolean embedded: boolean
openCollectiveApiKey: string openCollectiveApiKey: string
} }
@ -18,6 +19,7 @@ const initialAppState: AppState = {
assetsApi: {} as AssetsApi, assetsApi: {} as AssetsApi,
sideBarOpen: false, sideBarOpen: false,
sideBarSlim: false, sideBarSlim: false,
showThemeControl: false,
embedded: false, embedded: false,
openCollectiveApiKey: '', openCollectiveApiKey: '',
} }

View File

@ -0,0 +1,12 @@
import { useEffect } from 'react'
export const useTheme = (defaultTheme = 'default') => {
useEffect(() => {
const savedTheme = localStorage.getItem('theme')
const initialTheme = savedTheme ? (JSON.parse(savedTheme) as string) : defaultTheme
if (initialTheme !== 'default') {
document.documentElement.setAttribute('data-theme', defaultTheme)
localStorage.setItem('theme', JSON.stringify(initialTheme))
}
}, [defaultTheme])
}

View File

@ -53,39 +53,39 @@ export function LoginPage() {
}, [onLogin]) }, [onLogin])
return ( return (
<MapOverlayPage backdrop className='tw-max-w-xs tw-h-fit'> <MapOverlayPage backdrop className='tw:max-w-xs tw:h-fit'>
<h2 className='tw-text-2xl tw-font-semibold tw-mb-2 tw-text-center'>Login</h2> <h2 className='tw:text-2xl tw:font-semibold tw:mb-2 tw:text-center'>Login</h2>
<input <input
type='email' type='email'
placeholder='E-Mail' placeholder='E-Mail'
value={email} value={email}
onChange={(e) => setEmail(e.target.value)} onChange={(e) => setEmail(e.target.value)}
className='tw-input tw-input-bordered tw-w-full tw-max-w-xs' className='tw:input tw:input-bordered tw:w-full tw:max-w-xs'
/> />
<input <input
type='password' type='password'
placeholder='Password' placeholder='Password'
onChange={(e) => setPassword(e.target.value)} onChange={(e) => setPassword(e.target.value)}
className='tw-input tw-input-bordered tw-w-full tw-max-w-xs' className='tw:input tw:input-bordered tw:w-full tw:max-w-xs'
/> />
<div className='tw-text-right tw-text-primary'> <div className='tw:text-right tw:text-primary'>
<Link to='/reset-password'> <Link to='/reset-password'>
<span className='tw-text-sm tw-inline-block hover:tw-text-primary hover:tw-underline hover:tw-cursor-pointer tw-transition tw-duration-200'> <span className='tw:text-sm tw:inline-block tw:hover:text-primary tw:hover:underline tw:hover:cursor-pointer tw:transition tw:duration-200'>
Forgot Password? Forgot Password?
</span> </span>
</Link> </Link>
</div> </div>
<div className='tw-card-actions'> <div className='tw:card-actions'>
<button <button
className={ className={
loading loading
? 'tw-btn tw-btn-disabled tw-btn-block tw-btn-primary' ? 'tw:btn tw:btn-disabled tw:btn-block tw:btn-primary'
: 'tw-btn tw-btn-primary tw-btn-block' : 'tw:btn tw:btn-primary tw:btn-block'
} }
// eslint-disable-next-line @typescript-eslint/no-misused-promises // eslint-disable-next-line @typescript-eslint/no-misused-promises
onClick={() => onLogin()} onClick={() => onLogin()}
> >
{loading ? <span className='tw-loading tw-loading-spinner'></span> : 'Login'} {loading ? <span className='tw:loading tw:loading-spinner'></span> : 'Login'}
</button> </button>
</div> </div>
</MapOverlayPage> </MapOverlayPage>

View File

@ -36,26 +36,26 @@ export function RequestPasswordPage({ resetUrl }: { resetUrl: string }) {
} }
return ( return (
<MapOverlayPage backdrop className='tw-max-w-xs tw-h-fit'> <MapOverlayPage backdrop className='tw:max-w-xs tw:h-fit'>
<h2 className='tw-text-2xl tw-font-semibold tw-mb-2 tw-text-center'>Reset Password</h2> <h2 className='tw:text-2xl tw:font-semibold tw:mb-2 tw:text-center'>Reset Password</h2>
<input <input
type='email' type='email'
placeholder='E-Mail' placeholder='E-Mail'
value={email} value={email}
onChange={(e) => setEmail(e.target.value)} onChange={(e) => setEmail(e.target.value)}
className='tw-input tw-input-bordered tw-w-full tw-max-w-xs' className='tw:input tw:input-bordered tw:w-full tw:max-w-xs'
/> />
<div className='tw-card-actions tw-mt-4'> <div className='tw:card-actions tw:mt-4'>
<button <button
className={ className={
loading loading
? 'tw-btn tw-btn-disabled tw-btn-block tw-btn-primary' ? 'tw:btn tw:btn-disabled tw:btn-block tw:btn-primary'
: 'tw-btn tw-btn-primary tw-btn-block' : 'tw:btn tw:btn-primary tw:btn-block'
} }
// eslint-disable-next-line @typescript-eslint/no-misused-promises // eslint-disable-next-line @typescript-eslint/no-misused-promises
onClick={() => onReset()} onClick={() => onReset()}
> >
{loading ? <span className='tw-loading tw-loading-spinner'></span> : 'Send'} {loading ? <span className='tw:loading tw:loading-spinner'></span> : 'Send'}
</button> </button>
</div> </div>
</MapOverlayPage> </MapOverlayPage>

View File

@ -36,25 +36,25 @@ export function SetNewPasswordPage() {
} }
return ( return (
<MapOverlayPage backdrop className='tw-max-w-xs tw-h-fit'> <MapOverlayPage backdrop className='tw:max-w-xs tw:h-fit'>
<h2 className='tw-text-2xl tw-font-semibold tw-mb-2 tw-text-center'>Set new Password</h2> <h2 className='tw:text-2xl tw:font-semibold tw:mb-2 tw:text-center'>Set new Password</h2>
<input <input
type='password' type='password'
placeholder='Password' placeholder='Password'
onChange={(e) => setPassword(e.target.value)} onChange={(e) => setPassword(e.target.value)}
className='tw-input tw-input-bordered tw-w-full tw-max-w-xs' className='tw:input tw:input-bordered tw:w-full tw:max-w-xs'
/> />
<div className='tw-card-actions tw-mt-4'> <div className='tw:card-actions tw:mt-4'>
<button <button
className={ className={
loading loading
? 'tw-btn tw-btn-disabled tw-btn-block tw-btn-primary' ? 'tw:btn tw:btn-disabled tw:btn-block tw:btn-primary'
: 'tw-btn tw-btn-primary tw-btn-block' : 'tw:btn tw:btn-primary tw:btn-block'
} }
// eslint-disable-next-line @typescript-eslint/no-misused-promises // eslint-disable-next-line @typescript-eslint/no-misused-promises
onClick={() => onReset()} onClick={() => onReset()}
> >
{loading ? <span className='tw-loading tw-loading-spinner'></span> : 'Set'} {loading ? <span className='tw:loading tw:loading-spinner'></span> : 'Set'}
</button> </button>
</div> </div>
</MapOverlayPage> </MapOverlayPage>

View File

@ -55,39 +55,39 @@ export function SignupPage() {
}, [onRegister]) }, [onRegister])
return ( return (
<MapOverlayPage backdrop className='tw-max-w-xs tw-h-fit'> <MapOverlayPage backdrop className='tw:max-w-xs tw:h-fit'>
<h2 className='tw-text-2xl tw-font-semibold tw-mb-2 tw-text-center'>Sign Up</h2> <h2 className='tw:text-2xl tw:font-semibold tw:mb-2 tw:text-center'>Sign Up</h2>
<input <input
type='text' type='text'
placeholder='Name' placeholder='Name'
value={userName} value={userName}
onChange={(e) => setUserName(e.target.value)} onChange={(e) => setUserName(e.target.value)}
className='tw-input tw-input-bordered tw-w-full tw-max-w-xs' className='tw:input tw:input-bordered tw:w-full tw:max-w-xs'
/> />
<input <input
type='email' type='email'
placeholder='E-Mail' placeholder='E-Mail'
value={email} value={email}
onChange={(e) => setEmail(e.target.value)} onChange={(e) => setEmail(e.target.value)}
className='tw-input tw-input-bordered tw-w-full tw-max-w-xs' className='tw:input tw:input-bordered tw:w-full tw:max-w-xs'
/> />
<input <input
type='password' type='password'
placeholder='Password' placeholder='Password'
onChange={(e) => setPassword(e.target.value)} onChange={(e) => setPassword(e.target.value)}
className='tw-input tw-input-bordered tw-w-full tw-max-w-xs' className='tw:input tw:input-bordered tw:w-full tw:max-w-xs'
/> />
<div className='tw-card-actions tw-mt-4'> <div className='tw:card-actions tw:mt-4'>
<button <button
className={ className={
loading loading
? 'tw-btn tw-btn-disabled tw-btn-block tw-btn-primary' ? 'tw:btn tw:btn-disabled tw:btn-block tw:btn-primary'
: 'tw-btn tw-btn-primary tw-btn-block' : 'tw:btn tw:btn-primary tw:btn-block'
} }
// eslint-disable-next-line @typescript-eslint/no-misused-promises // eslint-disable-next-line @typescript-eslint/no-misused-promises
onClick={() => onRegister()} onClick={() => onRegister()}
> >
{loading ? <span className='tw-loading tw-loading-spinner'></span> : 'Sign Up'} {loading ? <span className='tw:loading tw:loading-spinner'></span> : 'Sign Up'}
</button> </button>
</div> </div>
</MapOverlayPage> </MapOverlayPage>

View File

@ -20,14 +20,14 @@ export function Modal({
return ( return (
<> <>
{/* You can open the modal using ID.showModal() method */} {/* You can open the modal using ID.showModal() method */}
<dialog id='my_modal_3' className='tw-modal tw-transition-all tw-duration-300'> <dialog id='my_modal_3' className='tw:modal tw:transition-all tw:duration-300'>
<form method='dialog' className='tw-modal-box tw-transition-none'> <form method='dialog' className='tw:modal-box tw:transition-none'>
<button className='tw-btn tw-btn-sm tw-btn-circle tw-btn-ghost tw-absolute tw-right-2 tw-top-2 focus:tw-outline-none'> <button className='tw:btn tw:btn-sm tw:btn-circle tw:btn-ghost tw:absolute tw:right-2 tw:top-2 tw:focus:outline-hidden'>
</button> </button>
{children} {children}
</form> </form>
<form method='dialog' className='tw-modal-backdrop'> <form method='dialog' className='tw:modal-backdrop'>
<button>close</button> <button>close</button>
</form> </form>
</dialog> </dialog>

View File

@ -39,56 +39,56 @@ export function Quests() {
return ( return (
<> <>
{questsOpen ? ( {questsOpen ? (
<div className='tw-card tw-w-48 tw-bg-base-100 tw-shadow-xl tw-absolute tw-bottom-4 tw-left-4 tw-z-[2000]'> <div className='tw:card tw:w-48 tw:bg-base-100 tw:shadow-xl tw:absolute tw:bottom-4 tw:left-4 tw:z-2000'>
<div className='tw-card-body tw-p-4 tw-pt-0'> <div className='tw:card-body tw:p-4 tw:pt-0'>
<div className='tw-card-actions tw-justify-end'> <div className='tw:card-actions tw:justify-end'>
<label <label
className='tw-btn tw-btn-sm tw-btn-circle tw-btn-ghost tw-absolute tw-right-1 tw-top-1' className='tw:btn tw:btn-sm tw:btn-circle tw:btn-ghost tw:absolute tw:right-1 tw:top-1'
onClick={() => setQuestsOpen(false)} onClick={() => setQuestsOpen(false)}
> >
</label> </label>
</div> </div>
<h2 className='tw-card-title tw-m-auto '> <h2 className='tw:card-title tw:m-auto '>
Level 1 Level 1
<QuestionMarkCircleIcon /> <QuestionMarkCircleIcon />
</h2> </h2>
<ul className='tw-flex-row'> <ul className='tw:flex-row'>
<li> <li>
<label className='tw-label tw-justify-normal tw-pt-1 tw-pb-0'> <label className='tw:label tw:justify-normal tw:pt-1 tw:pb-0'>
<input <input
type='checkbox' type='checkbox'
readOnly={true} readOnly={true}
className='tw-checkbox tw-checkbox-xs tw-checkbox-success' className='tw:checkbox tw:checkbox-xs tw:checkbox-success'
checked={isAuthenticated || false} checked={isAuthenticated || false}
/> />
<span className='tw-text-sm tw-label-text tw-mx-2'>Sign Up</span> <span className='tw:text-sm tw:label-text tw:mx-2'>Sign Up</span>
</label> </label>
</li> </li>
<li> <li>
<label className='tw-label tw-justify-normal tw-pt-1 tw-pb-0'> <label className='tw:label tw:justify-normal tw:pt-1 tw:pb-0'>
<input <input
type='checkbox' type='checkbox'
readOnly={true} readOnly={true}
className='tw-checkbox tw-checkbox-xs tw-checkbox-success' className='tw:checkbox tw:checkbox-xs tw:checkbox-success'
checked={!!profile?.text} checked={!!profile?.text}
/> />
<span className='tw-text-sm tw-label-text tw-mx-2'>Fill Profile</span> <span className='tw:text-sm tw:label-text tw:mx-2'>Fill Profile</span>
</label> </label>
</li> </li>
<li> <li>
<label className='tw-label tw-justify-normal tw-pt-1 tw-pb-0'> <label className='tw:label tw:justify-normal tw:pt-1 tw:pb-0'>
<input <input
type='checkbox' type='checkbox'
readOnly={true} readOnly={true}
className='tw-checkbox tw-checkbox-xs tw-checkbox-success' className='tw:checkbox tw:checkbox-xs tw:checkbox-success'
checked={!!profile?.image} checked={!!profile?.image}
/> />
<span className='tw-text-sm tw-label-text tw-mx-2'>Upload Avatar</span> <span className='tw:text-sm tw:label-text tw:mx-2'>Upload Avatar</span>
</label> </label>
</li> </li>
</ul> </ul>
{/** <button className='tw-btn tw-btn-xs tw-btn-neutral tw-w-fit tw-self-center tw-mt-1'>Next &gt;</button> */}{' '} {/** <button className='tw:btn tw:btn-xs tw:btn-neutral tw:w-fit tw:self-center tw:mt-1'>Next &gt;</button> */}{' '}
</div> </div>
</div> </div>
) : ( ) : (

View File

@ -91,9 +91,10 @@ export const Autocomplete = ({
onChange={(e) => handleChange(e)} onChange={(e) => handleChange(e)}
tabIndex='-1' tabIndex='-1'
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
className='tw:border-none tw:focus:outline-none tw:focus:ring-0 tw:mt-5'
/> />
<ul <ul
className={`tw-absolute tw-z-[4000] ${filteredSuggestions.length > 0 && 'tw-bg-base-100 tw-rounded-xl tw-p-2'}`} className={`tw:absolute tw:z-4000 ${filteredSuggestions.length > 0 && 'tw:bg-base-100 tw:rounded-xl tw:p-2'}`}
> >
{filteredSuggestions.map((suggestion, index) => ( {filteredSuggestions.map((suggestion, index) => (
<li key={index} onClick={() => handleSuggestionClick(suggestion)}> <li key={index} onClick={() => handleSuggestionClick(suggestion)}>

View File

@ -14,7 +14,7 @@ const ComboBoxInput = ({ id, options, value, onValueChange }: ComboBoxProps) =>
return ( return (
<select <select
id={id} id={id}
className='tw-form-select tw-block tw-w-full tw-py-2 tw-px-4 tw-border tw-border-gray-300 rounded-md tw-shadow-sm tw-text-sm focus:tw-outline-none focus:tw-ring-indigo-500 focus:tw-border-indigo-500 sm:tw-text-sm' className='tw:form-select tw:block tw:w-full tw:py-2 tw:px-4 tw:border tw:border-gray-300 rounded-md tw:shadow-sm tw:text-sm tw:focus:outline-hidden tw:focus:ring-indigo-500 tw:focus:border-indigo-500 tw:sm:text-sm'
onChange={handleChange} onChange={handleChange}
defaultValue={value} defaultValue={value}
> >

View File

@ -42,10 +42,10 @@ export function TextAreaInput({
} }
return ( return (
<div className={`tw-form-control tw-w-full ${containerStyle ?? ''}`}> <div className={`tw:form-control tw:w-full ${containerStyle ?? ''}`}>
{labelTitle ? ( {labelTitle ? (
<label className='tw-label'> <label className='tw:label'>
<span className={`tw-label-text tw-text-base-content ${labelStyle ?? ''}`}> <span className={`tw:label-text tw:text-base-content ${labelStyle ?? ''}`}>
{labelTitle} {labelTitle}
</span> </span>
</label> </label>
@ -55,7 +55,7 @@ export function TextAreaInput({
ref={ref} ref={ref}
value={inputValue} value={inputValue}
name={dataField} name={dataField}
className={`tw-textarea tw-textarea-bordered tw-w-full tw-leading-5 ${inputStyle ?? ''}`} className={`tw:textarea tw:textarea-bordered tw:w-full tw:leading-5 ${inputStyle ?? ''}`}
placeholder={placeholder ?? ''} placeholder={placeholder ?? ''}
onChange={handleChange} onChange={handleChange}
></textarea> ></textarea>

View File

@ -9,9 +9,9 @@ describe('<TextInput />', () => {
cy.get('input').should('have.attr', 'type', 'text') cy.get('input').should('have.attr', 'type', 'text')
cy.get('input').should('have.attr', 'placeholder', '') cy.get('input').should('have.attr', 'placeholder', '')
cy.get('input').should('have.attr', 'required') cy.get('input').should('have.attr', 'required')
cy.get('input').should('have.class', 'tw-input') cy.get('input').should('have.class', 'input')
cy.get('input').should('have.class', 'tw-input-bordered') cy.get('input').should('have.class', 'input-bordered')
cy.get('input').should('have.class', 'tw-w-full') cy.get('input').should('have.class', 'tw:w-full')
}) })
it('renders with given labelTitle', () => { it('renders with given labelTitle', () => {

View File

@ -47,10 +47,10 @@ export function TextInput({
} }
return ( return (
<div className={`tw-form-control ${containerStyle ?? ''}`}> <div className={`tw:form-control ${containerStyle ?? ''}`}>
{labelTitle ? ( {labelTitle ? (
<label className='tw-label'> <label className='tw:label'>
<span className={`tw-label-text tw-text-base-content ${labelStyle ?? ''}`}> <span className={`tw:label-text tw:text-base-content ${labelStyle ?? ''}`}>
{labelTitle} {labelTitle}
</span> </span>
</label> </label>
@ -64,7 +64,7 @@ export function TextInput({
placeholder={placeholder ?? ''} placeholder={placeholder ?? ''}
autoComplete={autocomplete} autoComplete={autocomplete}
onChange={handleChange} onChange={handleChange}
className={`tw-input tw-input-bordered tw-w-full ${inputStyle ?? ''}`} className={`tw:input tw:input-bordered tw:w-full ${inputStyle ?? ''}`}
/> />
</div> </div>
) )

View File

@ -2,7 +2,7 @@
exports[`<ComboBoxInput /> > renders properly 1`] = ` exports[`<ComboBoxInput /> > renders properly 1`] = `
<select <select
class="tw-form-select tw-block tw-w-full tw-py-2 tw-px-4 tw-border tw-border-gray-300 rounded-md tw-shadow-sm tw-text-sm focus:tw-outline-none focus:tw-ring-indigo-500 focus:tw-border-indigo-500 sm:tw-text-sm" class="tw:form-select tw:block tw:w-full tw:py-2 tw:px-4 tw:border tw:border-gray-300 rounded-md tw:shadow-sm tw:text-sm tw:focus:outline-hidden tw:focus:ring-indigo-500 tw:focus:border-indigo-500 tw:sm:text-sm"
> >
<option <option
value="Option 1" value="Option 1"

View File

@ -2,19 +2,19 @@
exports[`<TextAreaInput /> > labelTitle > sets label 1`] = ` exports[`<TextAreaInput /> > labelTitle > sets label 1`] = `
<div <div
class="tw-form-control tw-w-full " class="tw:form-control tw:w-full "
> >
<label <label
class="tw-label" class="tw:label"
> >
<span <span
class="tw-label-text tw-text-base-content " class="tw:label-text tw:text-base-content "
> >
My Title My Title
</span> </span>
</label> </label>
<textarea <textarea
class="tw-textarea tw-textarea-bordered tw-w-full tw-leading-5 " class="tw:textarea tw:textarea-bordered tw:w-full tw:leading-5 "
placeholder="" placeholder=""
required="" required=""
/> />
@ -23,10 +23,10 @@ exports[`<TextAreaInput /> > labelTitle > sets label 1`] = `
exports[`<TextAreaInput /> > renders properly 1`] = ` exports[`<TextAreaInput /> > renders properly 1`] = `
<div <div
class="tw-form-control tw-w-full " class="tw:form-control tw:w-full "
> >
<textarea <textarea
class="tw-textarea tw-textarea-bordered tw-w-full tw-leading-5 " class="tw:textarea tw:textarea-bordered tw:w-full tw:leading-5 "
placeholder="" placeholder=""
required="" required=""
/> />

View File

@ -2,19 +2,19 @@
exports[`<TextInput /> > labelTitle > sets label 1`] = ` exports[`<TextInput /> > labelTitle > sets label 1`] = `
<div <div
class="tw-form-control " class="tw:form-control "
> >
<label <label
class="tw-label" class="tw:label"
> >
<span <span
class="tw-label-text tw-text-base-content " class="tw:label-text tw:text-base-content "
> >
My Title My Title
</span> </span>
</label> </label>
<input <input
class="tw-input tw-input-bordered tw-w-full " class="tw:input tw:input-bordered tw:w-full "
placeholder="" placeholder=""
required="" required=""
type="text" type="text"
@ -25,10 +25,10 @@ exports[`<TextInput /> > labelTitle > sets label 1`] = `
exports[`<TextInput /> > renders properly 1`] = ` exports[`<TextInput /> > renders properly 1`] = `
<div <div
class="tw-form-control " class="tw:form-control "
> >
<input <input
class="tw-input tw-input-bordered tw-w-full " class="tw:input tw:input-bordered tw:w-full "
placeholder="" placeholder=""
required="" required=""
type="text" type="text"

View File

@ -31,11 +31,14 @@ export default function AddButton({
return ( return (
<> <>
{canAddItems() ? ( {canAddItems() ? (
<div className='tw-dropdown tw-dropdown-top tw-dropdown-end tw-dropdown-hover tw-z-500 tw-absolute tw-right-4 tw-bottom-4'> <div className='tw:dropdown tw:dropdown-top tw:dropdown-end tw:dropdown-hover tw:z-500 tw:absolute tw:right-4 tw:bottom-4'>
<label tabIndex={0} className='tw-z-500 tw-btn tw-btn-circle tw-shadow tw-bg-base-100'> <label
<SVG src={PlusSVG} className='tw-h-5 tw-w-5' /> tabIndex={0}
className='tw:z-500 tw:btn tw:btn-circle tw:btn-lg tw:shadow tw:bg-base-100'
>
<SVG src={PlusSVG} className='tw:h-5 tw:w-5' />
</label> </label>
<ul tabIndex={0} className='tw-dropdown-content tw-pr-1 tw-list-none'> <ul tabIndex={0} className='tw:dropdown-content tw:pr-1 tw:list-none'>
{layers.map( {layers.map(
(layer) => (layer) =>
layer.api?.createItem && layer.api?.createItem &&
@ -43,10 +46,10 @@ export default function AddButton({
layer.listed && ( layer.listed && (
<li key={layer.name}> <li key={layer.name}>
<a> <a>
<div className='tw-tooltip tw-tooltip-left' data-tip={layer.menuText}> <div className='tw:tooltip tw:tooltip-left' data-tip={layer.menuText}>
<button <button
tabIndex={0} tabIndex={0}
className='tw-z-500 tw-border-0 tw-pl-2 tw-p-0 tw-mb-3 tw-w-10 tw-h-10 tw-cursor-pointer tw-rounded-full tw-mouse tw-drop-shadow-md tw-transition tw-ease-in tw-duration-200 focus:tw-outline-none' className='tw:z-500 tw:border-0 tw:pl-2 tw:p-0 tw:mb-3 tw:w-10 tw:h-10 tw:cursor-pointer tw:rounded-full tw:mouse tw:drop-shadow-md tw:transition tw:ease-in tw:duration-200 tw:focus:outline-hidden'
style={{ backgroundColor: layer.menuColor || '#777' }} style={{ backgroundColor: layer.menuColor || '#777' }}
onClick={() => { onClick={() => {
triggerAction(layer) triggerAction(layer)
@ -58,7 +61,7 @@ export default function AddButton({
> >
<img <img
src={layer.menuIcon} src={layer.menuIcon}
className='tw-h-6 tw-w-6 tw-text-white' className='tw:h-6 tw:w-6 tw:text-white'
style={{ filter: 'invert(100%) brightness(200%)' }} style={{ filter: 'invert(100%) brightness(200%)' }}
/> />
</button> </button>

View File

@ -26,7 +26,7 @@ export const Control = ({
<div <div
ref={controlContainerRef} ref={controlContainerRef}
style={{ zIndex }} style={{ zIndex }}
className={`${absolute && 'tw-absolute'} tw-z-[999] tw-flex-col ${position === 'topLeft' && 'tw-top-4 tw-left-4'} ${position === 'bottomLeft' && 'tw-bottom-4 tw-left-4'} ${position === 'topRight' && 'tw-bottom-4 tw-right-4'} ${position === 'bottomRight' && 'tw-bottom-4 tw-right-4'}`} className={`${absolute && 'tw:absolute'} tw:z-999 tw:flex-col ${position === 'topLeft' && 'tw:top-4 tw:left-4'} ${position === 'bottomLeft' && 'tw:bottom-4 tw:left-4'} ${position === 'topRight' && 'tw:bottom-4 tw:right-4'} ${position === 'bottomRight' && 'tw:bottom-4 tw:right-4'}`}
> >
{children} {children}
</div> </div>

View File

@ -28,32 +28,32 @@ export function FilterControl() {
const visibleGroupTypes = useVisibleGroupType() const visibleGroupTypes = useVisibleGroupType()
return ( return (
<div className='tw-card tw-bg-base-100 tw-shadow-xl tw-mt-2 tw-w-fit'> <div className='tw:card tw:bg-base-100 tw:shadow-xl tw:mt-2 tw:w-fit'>
{open ? ( {open ? (
<div className='tw-card-body tw-pr-4 tw-min-w-[8rem] tw-p-2 tw-w-fit tw-transition-all tw-duration-300'> <div className='tw:card-body tw:pr-4 tw:min-w-[8rem] tw:p-2 tw:w-fit tw:transition-all tw:duration-300'>
<label <label
className='tw-btn tw-btn-sm tw-rounded-2xl tw-btn-circle tw-btn-ghost hover:tw-bg-transparent tw-absolute tw-right-0 tw-top-0 tw-text-gray-600' className='tw:btn tw:btn-sm tw:rounded-2xl tw:btn-circle tw:btn-ghost tw:hover:bg-transparent tw:absolute tw:right-0 tw:top-0 tw:text-gray-600'
onClick={() => { onClick={() => {
setOpen(false) setOpen(false)
}} }}
> >
<p className='tw-text-center '></p> <p className='tw:text-center '></p>
</label> </label>
<ul className='tw-flex-row'> <ul className='tw:flex-row'>
{groupTypes.map((groupType) => ( {groupTypes.map((groupType) => (
<li key={groupType.value}> <li key={groupType.value}>
<label <label
htmlFor={groupType.value} htmlFor={groupType.value}
className='tw-label tw-justify-normal tw-pt-1 tw-pb-1' className='tw:label tw:justify-normal tw:pt-1 tw:pb-1'
> >
<input <input
id={groupType.value} id={groupType.value}
onChange={() => toggleVisibleGroupType(groupType.value)} onChange={() => toggleVisibleGroupType(groupType.value)}
type='checkbox' type='checkbox'
className='tw-checkbox tw-checkbox-xs tw-checkbox-success' className='tw:checkbox tw:checkbox-xs tw:checkbox-success'
checked={isGroupTypeVisible(groupType.value)} checked={isGroupTypeVisible(groupType.value)}
/> />
<span className='tw-text-sm tw-label-text tw-mx-2 tw-cursor-pointer'> <span className='tw:text-sm tw:label-text tw:mx-2 tw:cursor-pointer'>
{groupType.text} {groupType.text}
</span> </span>
</label> </label>
@ -62,17 +62,17 @@ export function FilterControl() {
</ul> </ul>
</div> </div>
) : ( ) : (
<div className='tw-indicator'> <div className='tw:indicator'>
{visibleGroupTypes.length < groupTypes.length && ( {visibleGroupTypes.length < groupTypes.length && (
<span className='tw-indicator-item tw-badge tw-badge-success tw-h-4 tw-p-2 tw-translate-x-1/3 -tw-translate-y-1/3 tw-border-0'></span> <span className='tw:indicator-item tw:badge tw:badge-success tw:h-4 tw:p-2 tw:translate-x-1/3 tw:-translate-y-1/3 tw:border-0'></span>
)} )}
<div <div
className='tw-card-body hover:tw-bg-slate-300 tw-card tw-p-2 tw-h-10 tw-w-10 tw-transition-all tw-duration-300 hover:tw-cursor-pointer' className='tw:card-body tw:hover:bg-slate-300 tw:card tw:p-2 tw:h-10 tw:w-10 tw:transition-all tw:duration-300 tw:hover:cursor-pointer'
onClick={() => { onClick={() => {
setOpen(true) setOpen(true)
}} }}
> >
<FunnelIcon className='size-6 tw-stroke-[2.5]' /> <FunnelIcon className='size-6 tw:stroke-[2.5]' />
</div> </div>
</div> </div>
)} )}

View File

@ -9,15 +9,15 @@ export const GratitudeControl = () => {
if (isAuthenticated) { if (isAuthenticated) {
return ( return (
<div className='tw-card tw-bg-base-100 tw-shadow-xl tw-mt-2 tw-w-fit'> <div className='tw:card tw:bg-base-100 tw:shadow-xl tw:mt-2 tw:w-fit'>
{ {
<div <div
className='tw-card-body hover:tw-bg-slate-300 tw-card tw-p-2 tw-h-10 tw-w-10 tw-transition-all tw-duration-300 hover:tw-cursor-pointer' className='tw:card-body tw:hover:bg-slate-300 tw:card tw:p-2 tw:h-10 tw:w-10 tw:transition-all tw:duration-300 tw:hover:cursor-pointer'
onClick={() => { onClick={() => {
navigate('/select-user') navigate('/select-user')
}} }}
> >
<HeartIcon className='tw-stroke-[2.5]' /> <HeartIcon className='tw:stroke-[2.5]' />
</div> </div>
} }
</div> </div>

View File

@ -14,34 +14,34 @@ export function LayerControl() {
const toggleVisibleLayer = useToggleVisibleLayer() const toggleVisibleLayer = useToggleVisibleLayer()
return ( return (
<div className='tw-card tw-bg-base-100 tw-shadow-xl tw-mt-2 tw-w-fit'> <div className='tw:card tw:bg-base-100 tw:shadow-xl tw:mt-2 tw:w-fit'>
{open ? ( {open ? (
<div className='tw-card-body tw-pr-4 tw-min-w-[8rem] tw-p-2 tw-transition-all tw-w-fit tw-duration-300'> <div className='tw:card-body tw:pr-4 tw:min-w-[8rem] tw:p-2 tw:transition-all tw:w-fit tw:duration-300'>
<label <label
className='tw-btn tw-btn-sm tw-rounded-2xl tw-btn-circle tw-btn-ghost hover:tw-bg-transparent tw-absolute tw-right-0 tw-top-0 tw-text-gray-600' className='tw:btn tw:btn-sm tw:rounded-2xl tw:btn-circle tw:btn-ghost tw:hover:bg-transparent tw:absolute tw:right-0 tw:top-0 tw:text-gray-600'
onClick={() => { onClick={() => {
setOpen(false) setOpen(false)
}} }}
> >
<p className='tw-text-center '></p> <p className='tw:text-center '></p>
</label> </label>
<ul className='tw-flex-row'> <ul className='tw:flex-row'>
{layers.map( {layers.map(
(layer) => (layer) =>
layer.listed && ( layer.listed && (
<li key={layer.name}> <li key={layer.name}>
<label <label
htmlFor={layer.name} htmlFor={layer.name}
className='tw-label tw-justify-normal tw-pt-1 tw-pb-1' className='tw:label tw:justify-normal tw:pt-1 tw:pb-1 tw:text-base-content'
> >
<input <input
id={layer.name} id={layer.name}
onChange={() => toggleVisibleLayer(layer)} onChange={() => toggleVisibleLayer(layer)}
type='checkbox' type='checkbox'
className='tw-checkbox tw-checkbox-xs tw-checkbox-success' className='tw:checkbox tw:checkbox-xs tw:checkbox-success'
checked={isLayerVisible(layer)} checked={isLayerVisible(layer)}
/> />
<span className='tw-text-sm tw-label-text tw-mx-2 tw-cursor-pointer'> <span className='tw:text-sm tw:label-text tw:mx-2 tw:cursor-pointer'>
{layer.name} {layer.name}
</span> </span>
</label> </label>
@ -52,7 +52,7 @@ export function LayerControl() {
</div> </div>
) : ( ) : (
<div <div
className='tw-card-body hover:tw-bg-slate-300 tw-card tw-p-2 tw-h-10 tw-w-10 tw-transition-all tw-duration-300 hover:tw-cursor-pointer' className='tw:card-body tw:hover:bg-slate-300 tw:card tw:p-2 tw:h-10 tw:w-10 tw:transition-all tw:duration-300 tw:hover:cursor-pointer'
onClick={() => { onClick={() => {
setOpen(true) setOpen(true)
}} }}

View File

@ -43,9 +43,9 @@ export const LocateControl = () => {
return ( return (
<> <>
<div className='tw-card tw-h-12 tw-w-12 tw-bg-base-100 tw-shadow-xl tw-items-center tw-justify-center hover:tw-bg-slate-300 hover:tw-cursor-pointer tw-transition-all tw-duration-300 tw-ml-2'> <div className='tw:card tw:flex-none tw:h-12 tw:w-12 tw:bg-base-100 tw:shadow-xl tw:items-center tw:justify-center tw:hover:bg-slate-300 tw:hover:cursor-pointer tw:transition-all tw:duration-300 tw:ml-2'>
<div <div
className='tw-card-body tw-card tw-p-2 tw-h-10 tw-w-10 ' className='tw:card-body tw:card tw:p-2 tw:h-10 tw:w-10 '
onClick={() => { onClick={() => {
if (active) { if (active) {
lc.stop() lc.stop()
@ -57,11 +57,11 @@ export const LocateControl = () => {
}} }}
> >
{loading ? ( {loading ? (
<span className='tw-loading tw-loading-spinner tw-loading-md tw-mt-1'></span> <span className='tw:loading tw:loading-spinner tw:loading-md tw:mt-1'></span>
) : ( ) : (
<SVG <SVG
src={TargetSVG} src={TargetSVG}
className='tw-mt-1 tw-p-[1px]' className='tw:mt-1 tw:p-[1px]'
style={{ fill: `${active ? '#fc8702' : 'currentColor'}` }} style={{ fill: `${active ? '#fc8702' : 'currentColor'}` }}
/> />
)} )}

View File

@ -11,14 +11,14 @@ export function QuestControl() {
'' ''
) : ( ) : (
<div <div
className='tw-card tw-bg-base-100 tw-shadow-xl tw-my-2 tw-w-10' className='tw:card tw:bg-base-100 tw:shadow-xl tw:my-2 tw:w-10'
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
<div <div
className='tw-card-body hover:tw-bg-slate-300 tw-rounded-2xl tw-p-2 tw-h-10 tw-w-10 tw-transition-all tw-duration-300 hover:tw-cursor-pointer' className='tw:card-body tw:hover:bg-slate-300 tw:rounded-2xl tw:p-2 tw:h-10 tw:w-10 tw:transition-all tw:duration-300 tw:hover:cursor-pointer'
onClick={() => setQuestsOpen(true)} onClick={() => setQuestsOpen(true)}
> >
<img src={FistSVG} alt='Quests' className='tw-h-[2em]' /> <img src={FistSVG} alt='Quests' className='tw:h-[2em]' />
</div> </div>
</div> </div>
)} )}

View File

@ -103,16 +103,16 @@ export const SearchControl = () => {
return ( return (
<> <>
{!(windowDimensions.height < 500 && popupOpen && hideSuggestions) && ( {!(windowDimensions.height < 500 && popupOpen && hideSuggestions) && (
<div className='tw-w-[calc(100vw-2rem)] tw-max-w-[22rem] '> <div className='tw:w-[calc(100vw-2rem)] tw:max-w-[22rem] '>
<div className='tw-flex tw-flex-row'> <div className='tw:flex tw:flex-row'>
{appState.embedded && <SidebarControl />} {appState.embedded && <SidebarControl />}
<div className='tw-relative'> <div className='tw:relative tw:shrink tw:max-w-69 tw:w-full'>
<input <input
type='text' type='text'
placeholder='search ...' placeholder='search ...'
autoComplete='off' autoComplete='off'
value={value} value={value}
className='tw-input tw-input-bordered tw-grow tw-shadow-xl tw-rounded-lg tw-pr-12' className='tw:input tw:input-bordered tw:h-12 tw:grow tw:shadow-xl tw:rounded-box tw:pr-12 tw:w-full'
ref={searchInput} ref={searchInput}
onChange={(e) => setValue(e.target.value)} onChange={(e) => setValue(e.target.value)}
onFocus={() => { onFocus={() => {
@ -123,7 +123,7 @@ export const SearchControl = () => {
/> />
{value.length > 0 && ( {value.length > 0 && (
<button <button
className='tw-btn tw-btn-sm tw-btn-circle tw-absolute tw-right-2 tw-top-2' className='tw:btn tw:btn-sm tw:btn-circle tw:absolute tw:right-2 tw:top-2'
onClick={() => setValue('')} onClick={() => setValue('')}
> >
@ -140,13 +140,13 @@ export const SearchControl = () => {
value.length === 0 ? ( value.length === 0 ? (
'' ''
) : ( ) : (
<div className='tw-card tw-card-body tw-bg-base-100 tw-p-4 tw-mt-2 tw-shadow-xl tw-overflow-y-auto tw-max-h-[calc(100dvh-152px)] tw-absolute tw-z-3000'> <div className='tw:card tw:card-body tw:bg-base-100 tw:p-4 tw:mt-2 tw:shadow-xl tw:overflow-y-auto tw:max-h-[calc(100dvh-152px)] tw:absolute tw:z-3000 tw:w-83'>
{tagsResults.length > 0 && ( {tagsResults.length > 0 && (
<div className='tw-flex tw-flex-wrap'> <div className='tw:flex tw:flex-wrap'>
{tagsResults.slice(0, 3).map((tag) => ( {tagsResults.slice(0, 3).map((tag) => (
<div <div
key={tag.name} key={tag.name}
className='tw-rounded-2xl tw-text-white tw-p-1 tw-px-4 tw-shadow-md tw-card tw-mr-2 tw-mb-2 tw-cursor-pointer' className='tw:rounded-2xl tw:text-white tw:p-1 tw:px-4 tw:shadow-md tw:card tw:mr-2 tw:mb-2 tw:cursor-pointer'
style={{ backgroundColor: tag.color }} style={{ backgroundColor: tag.color }}
onClick={() => { onClick={() => {
addFilterTag(tag) addFilterTag(tag)
@ -159,12 +159,12 @@ export const SearchControl = () => {
)} )}
{itemsResults.length > 0 && tagsResults.length > 0 && ( {itemsResults.length > 0 && tagsResults.length > 0 && (
<hr className='tw-opacity-50'></hr> <hr className='tw:opacity-50'></hr>
)} )}
{itemsResults.slice(0, 5).map((item) => ( {itemsResults.slice(0, 5).map((item) => (
<div <div
key={item.id} key={item.id}
className='tw-cursor-pointer hover:tw-font-bold tw-flex tw-flex-row' className='tw:cursor-pointer tw:hover:font-bold tw:flex tw:flex-row'
onClick={() => { onClick={() => {
const marker = Object.entries(leafletRefs).find((r) => r[1].item === item)?.[1] const marker = Object.entries(leafletRefs).find((r) => r[1].item === item)?.[1]
.marker .marker
@ -180,7 +180,7 @@ export const SearchControl = () => {
{item.layer?.menuIcon ? ( {item.layer?.menuIcon ? (
<SVG <SVG
src={item.layer.menuIcon} src={item.layer.menuIcon}
className='tw-text-current tw-mr-2 tw-mt-0 tw-w-5' className='tw:text-current tw:mr-2 tw:mt-0 tw:w-5'
preProcessor={(code: string): string => { preProcessor={(code: string): string => {
code = code.replace(/fill=".*?"/g, 'fill="currentColor"') code = code.replace(/fill=".*?"/g, 'fill="currentColor"')
code = code.replace(/stroke=".*?"/g, 'stroke="currentColor"') code = code.replace(/stroke=".*?"/g, 'stroke="currentColor"')
@ -188,13 +188,13 @@ export const SearchControl = () => {
}} }}
/> />
) : ( ) : (
<div className='tw-w-5' /> <div className='tw:w-5' />
)} )}
<div> <div>
<div className='tw-text-sm tw-overflow-hidden tw-text-ellipsis tw-whitespace-nowrap tw-max-w-[17rem]'> <div className='tw:text-sm tw:overflow-hidden tw:text-ellipsis tw:whitespace-nowrap tw:max-w-[17rem]'>
{item.name} {item.name}
</div> </div>
<div className='tw-text-xs tw-overflow-hidden tw-text-ellipsis tw-whitespace-nowrap tw-max-w-[17rem]'> <div className='tw:text-xs tw:overflow-hidden tw:text-ellipsis tw:whitespace-nowrap tw:max-w-[17rem]'>
{item.text} {item.text}
</div> </div>
</div> </div>
@ -202,11 +202,11 @@ export const SearchControl = () => {
))} ))}
{Array.from(geoResults).length > 0 && {Array.from(geoResults).length > 0 &&
(itemsResults.length > 0 || tagsResults.length > 0) && ( (itemsResults.length > 0 || tagsResults.length > 0) && (
<hr className='tw-opacity-50'></hr> <hr className='tw:opacity-50'></hr>
)} )}
{Array.from(geoResults).map((geo) => ( {Array.from(geoResults).map((geo) => (
<div <div
className='tw-flex tw-flex-row hover:tw-font-bold tw-cursor-pointer' className='tw:flex tw:flex-row tw:hover:font-bold tw:cursor-pointer'
key={Math.random()} key={Math.random()}
onClick={() => { onClick={() => {
searchInput.current?.blur() searchInput.current?.blur()
@ -215,7 +215,7 @@ export const SearchControl = () => {
}) })
.addTo(map) .addTo(map)
.bindPopup( .bindPopup(
`<h3 class="tw-text-base tw-font-bold">${geo?.properties.name ? geo?.properties.name : value}<h3>${capitalizeFirstLetter(geo?.properties?.osm_value)}`, `<h3 class="tw:text-base tw:font-bold">${geo?.properties.name ? geo?.properties.name : value}<h3>${capitalizeFirstLetter(geo?.properties?.osm_value)}`,
) )
.openPopup() .openPopup()
.addEventListener('popupclose', (e) => { .addEventListener('popupclose', (e) => {
@ -238,12 +238,12 @@ export const SearchControl = () => {
hide() hide()
}} }}
> >
<MagnifyingGlassIcon className='tw-text-current tw-mr-2 tw-mt-0 tw-w-5' /> <MagnifyingGlassIcon className='tw:text-current tw:mr-2 tw:mt-0 tw:w-5' />
<div> <div>
<div className='tw-text-sm tw-overflow-hidden tw-text-ellipsis tw-whitespace-nowrap tw-max-w-[17rem]'> <div className='tw:text-sm tw:overflow-hidden tw:text-ellipsis tw:whitespace-nowrap tw:max-w-[17rem]'>
{geo?.properties.name ? geo?.properties.name : value} {geo?.properties.name ? geo?.properties.name : value}
</div> </div>
<div className='tw-text-xs tw-overflow-hidden tw-text-ellipsis tw-whitespace-nowrap tw-max-w-[17rem]'> <div className='tw:text-xs tw:overflow-hidden tw:text-ellipsis tw:whitespace-nowrap tw:max-w-[17rem]'>
{geo?.properties?.city && `${capitalizeFirstLetter(geo?.properties?.city)}, `}{' '} {geo?.properties?.city && `${capitalizeFirstLetter(geo?.properties?.city)}, `}{' '}
{geo?.properties?.osm_value && {geo?.properties?.osm_value &&
geo?.properties?.osm_value !== 'yes' && geo?.properties?.osm_value !== 'yes' &&
@ -261,7 +261,7 @@ export const SearchControl = () => {
))} ))}
{isGeoCoordinate(value) && ( {isGeoCoordinate(value) && (
<div <div
className='tw-flex tw-flex-row hover:tw-font-bold tw-cursor-pointer' className='tw:flex tw:flex-row tw:hover:font-bold tw:cursor-pointer'
onClick={() => { onClick={() => {
marker( marker(
new LatLng(extractCoordinates(value)![0], extractCoordinates(value)![1]), new LatLng(extractCoordinates(value)![0], extractCoordinates(value)![1]),
@ -271,7 +271,7 @@ export const SearchControl = () => {
) )
.addTo(map) .addTo(map)
.bindPopup( .bindPopup(
`<h3 class="tw-text-base tw-font-bold">${extractCoordinates(value)![0]}, ${extractCoordinates(value)![1]}</h3>`, `<h3 class="tw:text-base tw:font-bold">${extractCoordinates(value)![0]}, ${extractCoordinates(value)![1]}</h3>`,
) )
.openPopup() .openPopup()
.addEventListener('popupclose', (e) => { .addEventListener('popupclose', (e) => {
@ -285,12 +285,12 @@ export const SearchControl = () => {
) )
}} }}
> >
<FlagIcon className='tw-text-current tw-mr-2 tw-mt-0 tw-w-4' /> <FlagIcon className='tw:text-current tw:mr-2 tw:mt-0 tw:w-4' />
<div> <div>
<div className='tw-text-sm tw-overflow-hidden tw-text-ellipsis tw-whitespace-nowrap tw-max-w-[17rem]'> <div className='tw:text-sm tw:overflow-hidden tw:text-ellipsis tw:whitespace-nowrap tw:max-w-[17rem]'>
{value} {value}
</div> </div>
<div className='tw-text-xs tw-overflow-hidden tw-text-ellipsis tw-whitespace-nowrap tw-max-w-[17rem]'> <div className='tw:text-xs tw:overflow-hidden tw:text-ellipsis tw:whitespace-nowrap tw:max-w-[17rem]'>
{'Coordiante'} {'Coordiante'}
</div> </div>
</div> </div>

View File

@ -1,21 +1,21 @@
import Bars3Icon from '@heroicons/react/16/solid/Bars3Icon' import Bars3Icon from '@heroicons/react/16/solid/Bars3Icon'
import { useAppState, useSetAppState } from '#components/AppShell/hooks/useAppState'
// Converts leaflet.locatecontrol to a React Component // Converts leaflet.locatecontrol to a React Component
export const SidebarControl = () => { export const SidebarControl = () => {
const appState = useAppState()
const setAppState = useSetAppState()
const toggleSidebar = () => {
setAppState({ sideBarOpen: !appState.sideBarOpen })
}
return ( return (
<> <>
<div className='tw-card tw-bg-base-100 tw-shadow-xl tw-items-center tw-justify-center hover:tw-bg-slate-300 hover:tw-cursor-pointer tw-transition-all tw-duration-300 tw-mr-2 tw-h-12 tw-w-12 '> <div
<div className='tw-card-body tw-card tw-p-0'> className='tw:card tw:justify-center tw:items-center tw:bg-base-100 tw:flex-none tw:shadow-xl tw:px-0 tw:hover:bg-slate-300 tw:hover:cursor-pointer tw:transition-all tw:duration-300 tw:mr-2 tw:h-12 tw:w-12 '
<button onClick={() => toggleSidebar()}
className='tw-btn tw-btn-square tw-btn-ghost tw-rounded-2xl' >
data-te-sidenav-toggle-ref <Bars3Icon className='tw:inline-block tw:w-5 tw:h-5' />
data-te-target='#sidenav'
aria-controls='#sidenav'
aria-haspopup='true'
>
<Bars3Icon className='tw-inline-block tw-w-5 tw-h-5' />
</button>
</div>
</div> </div>
</> </>
) )

View File

@ -6,16 +6,16 @@ export const TagsControl = () => {
const removeFilterTag = useRemoveFilterTag() const removeFilterTag = useRemoveFilterTag()
return ( return (
<div className='tw-flex tw-flex-wrap tw-mt-4 tw-w-[calc(100vw-2rem)] tw-max-w-xs'> <div className='tw:flex tw:flex-wrap tw:mt-4 tw:w-[calc(100vw-2rem)] tw:max-w-xs'>
{filterTags.map((tag) => ( {filterTags.map((tag) => (
<div <div
key={tag.id} key={tag.id}
className='tw-rounded-2xl tw-text-white tw-p-2 tw-px-4 tw-shadow-xl tw-card tw-mr-2 tw-mb-2' className='tw:rounded-2xl tw:text-white tw:p-2 tw:px-4 tw:shadow-xl tw:card tw:mr-2 tw:mb-2'
style={{ backgroundColor: tag.color }} style={{ backgroundColor: tag.color }}
> >
<div className='tw-card-actions tw-justify-end'> <div className='tw:card-actions tw:justify-end'>
<label <label
className='tw-btn tw-btn-xs tw-btn-circle tw-absolute tw--right-2 tw--top-2 tw-bg-white tw-text-gray-600' className='tw:btn tw:btn-xs tw:btn-circle tw:absolute tw:-right-2 tw:-top-2 tw:bg-white tw:text-gray-600'
onClick={() => removeFilterTag(tag.name)} onClick={() => removeFilterTag(tag.name)}
> >

View File

@ -155,10 +155,10 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
> >
<form ref={formRef} onReset={resetPopup} autoComplete='off' onSubmit={(e) => handleSubmit(e)}> <form ref={formRef} onReset={resetPopup} autoComplete='off' onSubmit={(e) => handleSubmit(e)}>
{props.item ? ( {props.item ? (
<div className='tw-h-3'></div> <div className='tw:h-3'></div>
) : ( ) : (
<div className='tw-flex tw-justify-center'> <div className='tw:flex tw:justify-center'>
<b className='tw-text-xl tw-text-center tw-font-bold'>{props.layer.menuText}</b> <b className='tw:text-xl tw:text-center tw:font-bold'>{props.layer.menuText}</b>
</div> </div>
)} )}
@ -190,21 +190,21 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
placeholder='Text' placeholder='Text'
dataField='text' dataField='text'
defaultValue={props.item?.text ?? ''} defaultValue={props.item?.text ?? ''}
inputStyle='tw-h-40 tw-mt-5' inputStyle='tw:h-40 tw:mt-5'
/> />
</> </>
)} )}
<div className='tw-flex tw-justify-center'> <div className='tw:flex tw:justify-center'>
<button <button
className={ className={
spinner spinner
? 'tw-btn tw-btn-disabled tw-mt-5 tw-place-self-center' ? 'tw:btn tw:btn-disabled tw:mt-5 tw:place-self-center'
: 'tw-btn tw-mt-5 tw-place-self-center' : 'tw:btn tw:mt-5 tw:place-self-center'
} }
type='submit' type='submit'
> >
{spinner ? <span className='tw-loading tw-loading-spinner'></span> : 'Save'} {spinner ? <span className='tw:loading tw:loading-spinner'></span> : 'Save'}
</button> </button>
</div> </div>
</form> </form>

View File

@ -57,9 +57,7 @@ export function HeaderView({
const [imageLoaded, setImageLoaded] = useState(false) const [imageLoaded, setImageLoaded] = useState(false)
const avatar = const avatar = item.image && appState.assetsApi.url + item.image + '?width=160&heigth=160'
item.image &&
appState.assetsApi.url + item.image + `${big ? '?width=160&heigth=160' : '?width=80&heigth=80'}`
const title = item.name const title = item.name
const subtitle = item.subname const subtitle = item.subname
@ -74,18 +72,18 @@ export function HeaderView({
return ( return (
<> <>
<div className='tw-flex tw-flex-row'> <div className='tw:flex tw:flex-row'>
<div className={'tw-grow tw-max-w-[calc(100%-60px)] }'}> <div className={'tw:grow tw:max-w-[calc(100%-60px)] }'}>
<div className='flex items-center'> <div className='flex items-center'>
{avatar && ( {avatar && (
<div className='tw-avatar'> <div className='tw:avatar'>
<div <div
className={`${ className={`${
big ? 'tw-w-20' : 'tw-w-10' big ? 'tw:w-20' : 'tw:w-10'
} tw-inline tw-items-center tw-justify-center overflow-hidden`} } tw:inline tw:items-center tw:justify-center overflow-hidden`}
> >
<img <img
className={'tw-w-full tw-h-full tw-object-cover tw-rounded-full'} className={'tw:w-full tw:h-full tw:object-cover tw:rounded-full'}
src={avatar} src={avatar}
alt={item.name + ' logo'} alt={item.name + ' logo'}
onLoad={() => setImageLoaded(true)} onLoad={() => setImageLoaded(true)}
@ -93,53 +91,53 @@ export function HeaderView({
style={{ display: imageLoaded ? 'block' : 'none' }} style={{ display: imageLoaded ? 'block' : 'none' }}
/> />
{!imageLoaded && ( {!imageLoaded && (
<div className='tw-w-full tw-h-full tw-bg-gray-200 tw-rounded-full' /> <div className='tw:w-full tw:h-full tw:bg-gray-200 tw:rounded-full' />
)} )}
</div> </div>
</div> </div>
)} )}
<div className={`${avatar ? 'tw-ml-2' : ''} tw-overflow-hidden`}> <div className={`${avatar ? 'tw:ml-2' : ''} tw:overflow-hidden`}>
<div <div
className={`${big ? 'xl:tw-text-3xl tw-text-2xl' : 'tw-text-xl'} tw-font-semibold tw-truncate`} className={`${big ? 'tw:xl:text-3xl tw:text-2xl' : 'tw:text-xl'} tw:font-semibold tw:truncate`}
title={title} title={title}
> >
{title} {title}
</div> </div>
{showAddress && address && !hideSubname && ( {showAddress && address && !hideSubname && (
<div className={`tw-text-xs tw-text-gray-500 ${truncateSubname && 'tw-truncate'}`}> <div className={`tw:text-xs tw:text-gray-500 ${truncateSubname && 'tw:truncate'}`}>
{address} {address}
</div> </div>
)} )}
{subtitle && !hideSubname && ( {subtitle && !hideSubname && (
<div className={`tw-text-xs tw-text-gray-500 ${truncateSubname && 'tw-truncate'}`}> <div className={`tw:text-xs tw:opacity-50 ${truncateSubname && 'tw:truncate'}`}>
{subtitle} {subtitle}
</div> </div>
)} )}
</div> </div>
</div> </div>
</div> </div>
<div onClick={(e) => e.stopPropagation()} className={`${big ? 'tw-mt-5' : 'tw-mt-1'}`}> <div onClick={(e) => e.stopPropagation()} className={`${big ? 'tw:mt-5' : 'tw:mt-1'}`}>
{(api?.deleteItem || item.layer?.api?.updateItem) && {(api?.deleteItem || item.layer?.api?.updateItem) &&
(hasUserPermission(api?.collectionName!, 'delete', item) || (hasUserPermission(api?.collectionName!, 'delete', item) ||
hasUserPermission(api?.collectionName!, 'update', item)) && hasUserPermission(api?.collectionName!, 'update', item)) &&
!hideMenu && ( !hideMenu && (
<div className='tw-dropdown tw-dropdown-bottom'> <div className='tw:dropdown tw:dropdown-bottom'>
<label <label
tabIndex={0} tabIndex={0}
className='tw-bg-base-100 tw-btn tw-m-1 tw-leading-3 tw-border-none tw-min-h-0 tw-h-6' className='tw:bg-base-100 tw:btn tw:m-1 tw:leading-3 tw:border-none tw:min-h-0 tw:h-6'
> >
<EllipsisVerticalIcon className='tw-h-5 tw-w-5' /> <EllipsisVerticalIcon className='tw:h-5 tw:w-5' />
</label> </label>
<ul <ul
tabIndex={0} tabIndex={0}
className='tw-dropdown-content tw-menu tw-p-2 tw-shadow tw-bg-base-100 tw-rounded-box tw-z-1000' className='tw:dropdown-content tw:menu tw:p-2 tw:shadow tw:bg-base-100 tw:rounded-box tw:z-1000'
> >
{api?.updateItem && {api?.updateItem &&
hasUserPermission(api.collectionName!, 'update', item) && hasUserPermission(api.collectionName!, 'update', item) &&
editCallback && ( editCallback && (
<li> <li>
<a <a
className='!tw-text-base-content tw-cursor-pointer' className='tw:text-base-content! tw:cursor-pointer'
onClick={(e) => onClick={(e) =>
item.layer?.customEditLink item.layer?.customEditLink
? navigate( ? navigate(
@ -148,7 +146,7 @@ export function HeaderView({
: editCallback(e) : editCallback(e)
} }
> >
<PencilIcon className='tw-h-5 tw-w-5' /> <PencilIcon className='tw:h-5 tw:w-5' />
</a> </a>
</li> </li>
)} )}
@ -157,10 +155,10 @@ export function HeaderView({
setPositionCallback && ( setPositionCallback && (
<li> <li>
<a <a
className='!tw-text-base-content tw-cursor-pointer' className='tw:text-base-content! tw:cursor-pointer'
onClick={setPositionCallback} onClick={setPositionCallback}
> >
<SVG src={TargetDotSVG} className='tw-w-5 tw-h-5' /> <SVG src={TargetDotSVG} className='tw:w-5 tw:h-5' />
</a> </a>
</li> </li>
)} )}
@ -168,11 +166,11 @@ export function HeaderView({
hasUserPermission(api.collectionName!, 'delete', item) && hasUserPermission(api.collectionName!, 'delete', item) &&
deleteCallback && ( deleteCallback && (
<li> <li>
<a className='tw-cursor-pointer !tw-text-error' onClick={openDeleteModal}> <a className='tw:cursor-pointer tw:text-error!' onClick={openDeleteModal}>
{loading ? ( {loading ? (
<span className='tw-loading tw-loading-spinner tw-loading-sm'></span> <span className='tw:loading tw:loading-spinner tw:loading-sm'></span>
) : ( ) : (
<TrashIcon className='tw-h-5 tw-w-5' /> <TrashIcon className='tw:h-5 tw:w-5' />
)} )}
</a> </a>
</li> </li>
@ -192,10 +190,10 @@ export function HeaderView({
<span> <span>
Do you want to delete <b>{item.name}</b>? Do you want to delete <b>{item.name}</b>?
</span> </span>
<div className='tw-grid'> <div className='tw:grid'>
<div className='tw-flex tw-justify-between'> <div className='tw:flex tw:justify-between'>
<label <label
className='tw-btn tw-mt-4 tw-btn-error' className='tw:btn tw:mt-4 tw:btn-error'
onClick={(e) => { onClick={(e) => {
deleteCallback(e) deleteCallback(e)
setModalOpen(false) setModalOpen(false)
@ -203,7 +201,7 @@ export function HeaderView({
> >
Yes Yes
</label> </label>
<label className='tw-btn tw-mt-4' onClick={() => setModalOpen(false)}> <label className='tw:btn tw:mt-4' onClick={() => setModalOpen(false)}>
No No
</label> </label>
</div> </div>

View File

@ -29,7 +29,7 @@ export const PopupButton = ({
style={{ style={{
backgroundColor: `${item?.color ?? (item && (getItemTags(item) && getItemTags(item)[0] && getItemTags(item)[0].color ? getItemTags(item)[0].color : (item?.layer?.markerDefaultColor ?? '#000')))}`, backgroundColor: `${item?.color ?? (item && (getItemTags(item) && getItemTags(item)[0] && getItemTags(item)[0].color ? getItemTags(item)[0].color : (item?.layer?.markerDefaultColor ?? '#000')))}`,
}} }}
className='tw-btn tw-text-white tw-btn-sm tw-float-right tw-mt-1' className='tw:btn tw:text-white tw:btn-sm tw:float-right tw:mt-1'
> >
{text} {text}
</button> </button>

View File

@ -13,15 +13,15 @@ export const PopupCheckboxInput = ({
item?: Item item?: Item
}) => { }) => {
return ( return (
<label htmlFor={item?.id} className='tw-label tw-justify-normal tw-pt-1 tw-pb-1'> <label htmlFor={item?.id} className='tw:label tw:justify-normal tw:pt-1 tw:pb-1'>
<input <input
id={item?.id} id={item?.id}
type='checkbox' type='checkbox'
name={dataField} name={dataField}
className='tw-checkbox tw-checkbox-xs tw-checkbox-success' className='tw:checkbox tw:checkbox-xs tw:checkbox-success'
checked={item?.public_edit} checked={item?.public_edit}
/> />
<span className='tw-text-sm tw-label-text tw-mx-2 tw-cursor-pointer'>{label}</span> <span className='tw:text-sm tw:label-text tw:mx-2 tw:cursor-pointer'>{label}</span>
</label> </label>
) )
} }

View File

@ -20,12 +20,12 @@ export const PopupStartEndInput = ({
updateEndValue, updateEndValue,
}: StartEndInputProps) => { }: StartEndInputProps) => {
return ( return (
<div className='tw-grid tw-grid-cols-2 tw-gap-2'> <div className='tw:grid tw:grid-cols-2 tw:gap-2'>
<TextInput <TextInput
type='date' type='date'
placeholder='start' placeholder='start'
dataField='start' dataField='start'
inputStyle='tw-text-sm tw-px-2' inputStyle='tw:text-sm tw:px-2'
labelTitle={showLabels ? 'start' : ''} labelTitle={showLabels ? 'start' : ''}
defaultValue={item && item.start ? item.start.substring(0, 10) : ''} defaultValue={item && item.start ? item.start.substring(0, 10) : ''}
autocomplete='one-time-code' autocomplete='one-time-code'
@ -35,7 +35,7 @@ export const PopupStartEndInput = ({
type='date' type='date'
placeholder='end' placeholder='end'
dataField='end' dataField='end'
inputStyle='tw-text-sm tw-px-2' inputStyle='tw:text-sm tw:px-2'
labelTitle={showLabels ? 'end' : ''} labelTitle={showLabels ? 'end' : ''}
defaultValue={item && item.end ? item.end.substring(0, 10) : ''} defaultValue={item && item.end ? item.end.substring(0, 10) : ''}
autocomplete='one-time-code' autocomplete='one-time-code'

View File

@ -23,7 +23,7 @@ export const PopupTextInput = ({
placeholder={placeholder} placeholder={placeholder}
inputStyle={style} inputStyle={style}
type='text' type='text'
containerStyle={'tw-mt-4'} containerStyle={'tw:mt-4'}
></TextInput> ></TextInput>
) )
} }

View File

@ -8,23 +8,23 @@ import type { Item } from '#types/Item'
*/ */
export const StartEndView = ({ item }: { item?: Item }) => { export const StartEndView = ({ item }: { item?: Item }) => {
return ( return (
<div className='tw-flex tw-flex-row tw-mb-4 tw-mt-1'> <div className='tw:flex tw:flex-row tw:mb-4 tw:mt-1'>
<div className='tw-basis-2/5 tw-flex tw-flex-row'> <div className='tw:basis-2/5 tw:flex tw:flex-row'>
<CalendarIcon className='tw-h-4 tw-w-4 tw-mr-2' /> <CalendarIcon className='tw:h-4 tw:w-4 tw:mr-2' />
<time <time
className='tw-align-middle' className='tw:align-middle'
dateTime={item && item.start ? item.start.substring(0, 10) : ''} dateTime={item && item.start ? item.start.substring(0, 10) : ''}
> >
{item && item.start ? new Date(item.start).toLocaleDateString() : ''} {item && item.start ? new Date(item.start).toLocaleDateString() : ''}
</time> </time>
</div> </div>
<div className='tw-basis-1/5 tw-place-content-center'> <div className='tw:basis-1/5 tw:place-content-center'>
<span>-</span> <span>-</span>
</div> </div>
<div className='tw-basis-2/5 tw-flex tw-flex-row'> <div className='tw:basis-2/5 tw:flex tw:flex-row'>
<CalendarIcon className='tw-h-4 tw-w-4 tw-mr-2' /> <CalendarIcon className='tw:h-4 tw:w-4 tw:mr-2' />
<time <time
className='tw-align-middle' className='tw:align-middle'
dateTime={item && item.end ? item.end.substring(0, 10) : ''} dateTime={item && item.end ? item.end.substring(0, 10) : ''}
> >
{item && item.end ? new Date(item.end).toLocaleDateString() : ''} {item && item.end ? new Date(item.end).toLocaleDateString() : ''}

View File

@ -87,36 +87,36 @@ export const TextView = ({
}) })
} }
const CustomH1 = ({ children }) => <h1 className='tw-text-xl tw-font-bold'>{children}</h1> const CustomH1 = ({ children }) => <h1 className='tw:text-xl tw:font-bold'>{children}</h1>
const CustomH2 = ({ children }) => <h2 className='tw-text-lg tw-font-bold'>{children}</h2> const CustomH2 = ({ children }) => <h2 className='tw:text-lg tw:font-bold'>{children}</h2>
const CustomH3 = ({ children }) => <h3 className='tw-text-base tw-font-bold'>{children}</h3> const CustomH3 = ({ children }) => <h3 className='tw:text-base tw:font-bold'>{children}</h3>
const CustomH4 = ({ children }) => <h4 className='tw-text-base tw-font-bold'>{children}</h4> const CustomH4 = ({ children }) => <h4 className='tw:text-base tw:font-bold'>{children}</h4>
const CustomH5 = ({ children }) => <h5 className='tw-text-sm tw-font-bold'>{children}</h5> const CustomH5 = ({ children }) => <h5 className='tw:text-sm tw:font-bold'>{children}</h5>
const CustomH6 = ({ children }) => <h6 className='tw-text-sm tw-font-bold'>{children}</h6> const CustomH6 = ({ children }) => <h6 className='tw:text-sm tw:font-bold'>{children}</h6>
const CustomParagraph = ({ children }) => <p className='!tw-my-2'>{children}</p> const CustomParagraph = ({ children }) => <p className='tw:my-2!'>{children}</p>
const CustomUnorderdList = ({ children }) => ( const CustomUnorderdList = ({ children }) => (
<ul className='tw-list-disc tw-list-inside'>{children}</ul> <ul className='tw:list-disc tw:list-inside'>{children}</ul>
) )
const CustomOrderdList = ({ children }) => ( const CustomOrderdList = ({ children }) => (
<ol className='tw-list-decimal tw-list-inside'>{children}</ol> <ol className='tw:list-decimal tw:list-inside'>{children}</ol>
) )
const CustomHorizontalRow = ({ children }) => <hr className='tw-border-current'>{children}</hr> const CustomHorizontalRow = ({ children }) => <hr className='tw:border-current'>{children}</hr>
// eslint-disable-next-line react/prop-types // eslint-disable-next-line react/prop-types
const CustomImage = ({ alt, src, title }) => ( const CustomImage = ({ alt, src, title }) => (
<img className='tw-max-w-full tw-rounded tw-shadow' src={src} alt={alt} title={title} /> <img className='tw:max-w-full tw:rounded tw:shadow' src={src} alt={alt} title={title} />
) )
const CustomExternalLink = ({ href, children }) => ( const CustomExternalLink = ({ href, children }) => (
<a className='tw-font-bold tw-underline' href={href} target='_blank' rel='noreferrer'> <a className='tw:font-bold tw:underline' href={href} target='_blank' rel='noreferrer'>
{' '} {' '}
{children} {children}
</a> </a>
@ -148,7 +148,7 @@ export const TextView = ({
// eslint-disable-next-line react/display-name // eslint-disable-next-line react/display-name
const MemoizedVideoEmbed = memo(({ url }: { url: string }) => ( const MemoizedVideoEmbed = memo(({ url }: { url: string }) => (
<iframe <iframe
className='tw-w-full' className='tw:w-full'
src={url} src={url}
allow='fullscreen; picture-in-picture' allow='fullscreen; picture-in-picture'
allowFullScreen allowFullScreen
@ -157,7 +157,7 @@ export const TextView = ({
return ( return (
<Markdown <Markdown
className={'tw-text-map tw-leading-map tw-text-sm'} className={'tw:text-map tw:leading-map tw:text-sm'}
remarkPlugins={[remarkBreaks]} remarkPlugins={[remarkBreaks]}
components={{ components={{
p: CustomParagraph, p: CustomParagraph,

View File

@ -84,7 +84,7 @@ export const ItemViewPopup = forwardRef((props: ItemViewPopupProps, ref: any) =>
return ( return (
<LeafletPopup ref={ref} maxHeight={377} minWidth={275} maxWidth={275} autoPanPadding={[20, 80]}> <LeafletPopup ref={ref} maxHeight={377} minWidth={275} maxWidth={275} autoPanPadding={[20, 80]}>
<div className='tw-bg-base-100 tw-text-base-content'> <div className='tw:bg-base-100 tw:text-base-content'>
<HeaderView <HeaderView
api={props.item.layer?.api} api={props.item.layer?.api}
item={props.item} item={props.item}
@ -97,7 +97,7 @@ export const ItemViewPopup = forwardRef((props: ItemViewPopupProps, ref: any) =>
}} }}
loading={loading} loading={loading}
/> />
<div className='tw-overflow-y-auto tw-overflow-x-hidden tw-max-h-64 fade'> <div className='tw:overflow-y-auto tw:overflow-x-hidden tw:max-h-64 fade'>
{props.children ? ( {props.children ? (
Children.toArray(props.children).map((child) => Children.toArray(props.children).map((child) =>
isValidElement<{ item: Item; test: string }>(child) isValidElement<{ item: Item; test: string }>(child)
@ -108,22 +108,22 @@ export const ItemViewPopup = forwardRef((props: ItemViewPopupProps, ref: any) =>
<TextView text={props.item.text} itemId={props.item.id} /> <TextView text={props.item.text} itemId={props.item.id} />
)} )}
</div> </div>
<div className='tw-flex -tw-mb-1 tw-flex-row tw-mr-2 tw-mt-1'> <div className='tw:flex tw:-mb-1 tw:flex-row tw:mr-2 tw:mt-1'>
{infoExpanded ? ( {infoExpanded ? (
<p <p
className={'tw-italic tw-min-h-[21px] !tw-my-0 tw-text-gray-500'} className={'tw:italic tw:min-h-[21px] tw:my-0! tw:opacity-50'}
>{`${props.item.date_updated && props.item.date_updated !== props.item.date_created ? 'updated' : 'posted'} ${props.item && props.item.user_created && props.item.user_created.first_name ? `by ${props.item.user_created.first_name}` : ''} ${props.item.date_updated ? timeAgo(props.item.date_updated) : timeAgo(props.item.date_created!)}`}</p> >{`${props.item.date_updated && props.item.date_updated !== props.item.date_created ? 'updated' : 'posted'} ${props.item && props.item.user_created && props.item.user_created.first_name ? `by ${props.item.user_created.first_name}` : ''} ${props.item.date_updated ? timeAgo(props.item.date_updated) : timeAgo(props.item.date_created!)}`}</p>
) : ( ) : (
<p <p
className='!tw-my-0 tw-min-h-[21px] tw-font-bold tw-cursor-pointer tw-text-gray-500' className='tw:my-0! tw:min-h-[21px] tw:font-bold tw:cursor-pointer tw:text-gray-500'
onClick={() => setInfoExpanded(true)} onClick={() => setInfoExpanded(true)}
> >
</p> </p>
)} )}
<div className='tw-grow'></div> <div className='tw:grow'></div>
{ {
//* * <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="tw-place-self-end tw-w-4 tw-h-4 tw-mb-1 tw-cursor-pointer"><path strokeLinecap="round" strokeLinejoin="round" d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15" /></svg> */ //* * <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="tw:place-self-end tw:w-4 tw:h-4 tw:mb-1 tw:cursor-pointer"><path strokeLinecap="round" strokeLinejoin="round" d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15" /></svg> */
} }
</div> </div>
</div> </div>

View File

@ -1,18 +1,18 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
export const SelectPosition = ({ setSelectNewItemPosition }: { setSelectNewItemPosition }) => { export const SelectPosition = ({ setSelectNewItemPosition }: { setSelectNewItemPosition }) => {
return ( return (
<div className='tw-animate-pulseGrow tw-button tw-z-1000 tw-absolute tw-right-5 tw-top-4 tw-drop-shadow-md'> <div className='tw:animate-pulseGrow tw:button tw:z-1000 tw:absolute tw:right-5 tw:top-4 tw:drop-shadow-md'>
<label <label
className='tw-btn tw-btn-sm tw-rounded-2xl tw-btn-circle tw-btn-ghost hover:tw-bg-transparent tw-absolute tw-right-0 tw-top-0 tw-text-gray-600' className='tw:btn tw:btn-sm tw:rounded-2xl tw:btn-circle tw:btn-ghost tw:hover:bg-transparent tw:absolute tw:right-0 tw:top-0 tw:text-gray-600'
onClick={() => { onClick={() => {
setSelectNewItemPosition(null) setSelectNewItemPosition(null)
}} }}
> >
<p className='tw-text-center '></p> <p className='tw:text-center '></p>
</label> </label>
<div className='tw-alert tw-bg-base-100 tw-text-base-content'> <div className='tw:alert tw:bg-base-100 tw:text-base-content'>
<div> <div>
<span className='tw-text-lg'>Select position on the map!</span> <span className='tw:text-lg'>Select position on the map!</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -51,6 +51,8 @@ function UtopiaMap({
showFilterControl = false, showFilterControl = false,
showGratitudeControl = false, showGratitudeControl = false,
showLayerControl = true, showLayerControl = true,
showThemeControl = false,
defaultTheme,
donationWidget, donationWidget,
}: { }: {
/** height of the map (default '500px') */ /** height of the map (default '500px') */
@ -71,6 +73,10 @@ function UtopiaMap({
showLayerControl?: boolean showLayerControl?: boolean
/** show the layer control widget (default true) */ /** show the layer control widget (default true) */
showGratitudeControl?: boolean showGratitudeControl?: boolean
/** show a widget to switch the theme */
showThemeControl?: boolean
/** the defaut theme */
defaultTheme?: string
/** ask to donate to the Utopia Project OpenCollective campaign (default false) */ /** ask to donate to the Utopia Project OpenCollective campaign (default false) */
donationWidget?: boolean donationWidget?: boolean
}) { }) {
@ -89,6 +95,8 @@ function UtopiaMap({
showGratitudeControl={showGratitudeControl} showGratitudeControl={showGratitudeControl}
showLayerControl={showLayerControl} showLayerControl={showLayerControl}
donationWidget={donationWidget} donationWidget={donationWidget}
showThemeControl={showThemeControl}
defaultTheme={defaultTheme}
> >
{children} {children}
</UtopiaMapInner> </UtopiaMapInner>

View File

@ -12,6 +12,8 @@ import MarkerClusterGroup from 'react-leaflet-cluster'
import { Outlet, useLocation } from 'react-router-dom' import { Outlet, useLocation } from 'react-router-dom'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { useSetAppState } from '#components/AppShell/hooks/useAppState'
import { useTheme } from '#components/AppShell/hooks/useTheme'
import { containsUUID } from '#utils/ContainsUUID' import { containsUUID } from '#utils/ContainsUUID'
import { useClusterRef, useSetClusterRef } from './hooks/useClusterRef' import { useClusterRef, useSetClusterRef } from './hooks/useClusterRef'
@ -42,6 +44,8 @@ export function UtopiaMapInner({
showFilterControl = false, showFilterControl = false,
showGratitudeControl = false, showGratitudeControl = false,
showLayerControl = true, showLayerControl = true,
showThemeControl = false,
defaultTheme = '',
donationWidget, donationWidget,
}: { }: {
children?: React.ReactNode children?: React.ReactNode
@ -50,6 +54,8 @@ export function UtopiaMapInner({
showLayerControl?: boolean showLayerControl?: boolean
showGratitudeControl?: boolean showGratitudeControl?: boolean
donationWidget?: boolean donationWidget?: boolean
showThemeControl?: boolean
defaultTheme?: string
}) { }) {
const selectNewItemPosition = useSelectPosition() const selectNewItemPosition = useSelectPosition()
const setSelectNewItemPosition = useSetSelectPosition() const setSelectNewItemPosition = useSetSelectPosition()
@ -58,6 +64,8 @@ export function UtopiaMapInner({
const setMapClicked = useSetMapClicked() const setMapClicked = useSetMapClicked()
const [itemFormPopup, setItemFormPopup] = useState<ItemFormPopupProps | null>(null) const [itemFormPopup, setItemFormPopup] = useState<ItemFormPopupProps | null>(null)
useTheme(defaultTheme)
const layers = useLayers() const layers = useLayers()
const addVisibleLayer = useAddVisibleLayer() const addVisibleLayer = useAddVisibleLayer()
const leafletRefs = useLeafletRefs() const leafletRefs = useLeafletRefs()
@ -70,6 +78,12 @@ export function UtopiaMapInner({
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [layers]) }, [layers])
const setAppState = useSetAppState()
useEffect(() => {
setAppState({ showThemeControl })
}, [setAppState, showThemeControl])
const init = useRef(false) const init = useRef(false)
useEffect(() => { useEffect(() => {
if (!init.current) { if (!init.current) {
@ -86,7 +100,7 @@ export function UtopiaMapInner({
} }
/> />
<a href='https://opencollective.com/utopia-project'> <a href='https://opencollective.com/utopia-project'>
<div className='tw-btn tw-btn-sm tw-float-right tw-btn-primary'>Donate</div> <div className='tw:btn tw:btn-sm tw:float-right tw:btn-primary'>Donate</div>
</a> </a>
</div> </div>
</>, </>,
@ -187,7 +201,7 @@ export function UtopiaMapInner({
} }
return ( return (
<div className={`tw-h-full ${selectNewItemPosition != null ? 'crosshair-cursor-enabled' : ''}`}> <div className={`tw:h-full ${selectNewItemPosition != null ? 'crosshair-cursor-enabled' : ''}`}>
<Outlet /> <Outlet />
<Control position='topLeft' zIndex='1000' absolute> <Control position='topLeft' zIndex='1000' absolute>
<SearchControl /> <SearchControl />

View File

@ -157,10 +157,10 @@ export function ProfileForm() {
<> <>
<MapOverlayPage <MapOverlayPage
backdrop backdrop
className='tw-mx-4 tw-mt-4 tw-mb-4 tw-overflow-x-hidden tw-w-[calc(100%-32px)] md:tw-w-[calc(50%-32px)] tw-max-w-3xl !tw-left-auto tw-top-0 tw-bottom-0' className='tw:mx-4 tw:mt-4 tw:mb-4 tw:overflow-x-hidden tw:w-[calc(100%-32px)] tw:md:w-[calc(50%-32px)] tw:max-w-3xl tw:left-auto! tw:top-0 tw:bottom-0'
> >
<form <form
className='tw-h-full' className='tw:h-full'
onSubmit={(e) => { onSubmit={(e) => {
e.preventDefault() e.preventDefault()
void onUpdateItem( void onUpdateItem(
@ -177,7 +177,7 @@ export function ProfileForm() {
) )
}} }}
> >
<div className='tw-flex tw-flex-col tw-h-full'> <div className='tw:flex tw:flex-col tw:h-full'>
<FormHeader item={item} state={state} setState={setState} /> <FormHeader item={item} state={state} setState={setState} />
{template === 'onepager' && ( {template === 'onepager' && (
@ -203,9 +203,9 @@ export function ProfileForm() {
></TabsForm> ></TabsForm>
)} )}
<div className='tw-mt-4'> <div className='tw:mt-4 tw:flex-none'>
<button <button
className={loading ? ' tw-loading tw-btn tw-float-right' : 'tw-btn tw-float-right'} className={`${loading ? ' tw:loading tw:btn tw:float-right' : 'tw:btn tw:float-right'}`}
type='submit' type='submit'
style={{ style={{
// We could refactor this, it is used several times at different locations // We could refactor this, it is used several times at different locations

View File

@ -174,10 +174,10 @@ export function ProfileView({ attestationApi }: { attestationApi?: ItemsApi<any>
{item && ( {item && (
<MapOverlayPage <MapOverlayPage
key={item.id} key={item.id}
className={`!tw-p-0 tw-mx-4 tw-mt-4 tw-mb-4 md:tw-w-[calc(50%-32px)] tw-w-[calc(100%-32px)] tw-min-w-80 tw-max-w-3xl !tw-left-0 sm:!tw-left-auto tw-top-0 tw-bottom-0 tw-transition-opacity tw-duration-500 ${!selectPosition ? 'tw-opacity-100 tw-pointer-events-auto' : 'tw-opacity-0 tw-pointer-events-none'}`} className={`tw:p-0! tw:overflow-scroll tw:m-4! tw:md:w-[calc(50%-32px)] tw:w-[calc(100%-32px)] tw:min-w-80 tw:max-w-3xl tw:left-0! tw:sm:left-auto! tw:top-0 tw:bottom-0 tw:transition-opacity tw:duration-500 ${!selectPosition ? 'tw:opacity-100 tw:pointer-events-auto' : 'tw:opacity-0 tw:pointer-events-none'}`}
> >
<> <>
<div className={'tw-px-6 tw-pt-6'}> <div className={'tw:px-6 tw:pt-6'}>
<HeaderView <HeaderView
api={item.layer?.api} api={item.layer?.api}
item={item} item={item}

View File

@ -54,11 +54,11 @@ export function ActionButton({
<> <>
{hasUserPermission(collection, 'update', item) && ( {hasUserPermission(collection, 'update', item) && (
<> <>
<div className={`tw-absolute tw-right-4 tw-bottom-4 tw-flex tw-flex-col ${customStyle}`}> <div className={`tw:absolute tw:right-6 tw:bottom-4 tw:flex tw:flex-col ${customStyle}`}>
{triggerItemSelected && ( {triggerItemSelected && (
<button <button
tabIndex={0} tabIndex={0}
className='tw-z-500 tw-btn tw-btn-circle tw-shadow' className='tw:z-500 tw:btn tw:btn-circle tw:shadow'
onClick={() => { onClick={() => {
setModalOpen(true) setModalOpen(true)
}} }}
@ -67,13 +67,13 @@ export function ActionButton({
color: '#fff', color: '#fff',
}} }}
> >
<LinkIcon className='tw-h-5 tw-w-5 tw-stroke-[2.5]' /> <LinkIcon className='tw:h-5 tw:w-5 tw:stroke-[2.5]' />
</button> </button>
)} )}
{triggerAddButton && ( {triggerAddButton && (
<button <button
tabIndex={0} tabIndex={0}
className='tw-z-500 tw-btn tw-btn-circle tw-shadow tw-mt-2' className='tw:z-500 tw:btn tw:btn-circle tw:shadow tw:mt-2'
onClick={() => { onClick={() => {
triggerAddButton() triggerAddButton()
}} }}
@ -82,7 +82,7 @@ export function ActionButton({
color: '#fff', color: '#fff',
}} }}
> >
<PlusIcon className='tw-w-5 tw-h-5 tw-stroke-[2.5]' /> <PlusIcon className='tw:w-5 tw:h-5 tw:stroke-[2.5]' />
</button> </button>
)} )}
</div> </div>
@ -90,17 +90,17 @@ export function ActionButton({
title={'Select'} title={'Select'}
isOpened={modalOpen} isOpened={modalOpen}
onClose={() => setModalOpen(false)} onClose={() => setModalOpen(false)}
className='tw-w-xl sm:tw-w-2xl tw-min-h-80 tw-bg-base-200' className='tw:w-xl tw:sm:w-2xl tw:min-h-80 tw:bg-base-200'
> >
<TextInput <TextInput
defaultValue='' defaultValue=''
placeholder='🔍 Search' placeholder='🔍 Search'
containerStyle='lg:col-span-2 tw-m-4 ' containerStyle='lg:col-span-2 tw:m-4 '
updateFormValue={(val) => { updateFormValue={(val) => {
setSearch(val) setSearch(val)
}} }}
></TextInput> ></TextInput>
<div className='tw-grid tw-grid-cols-1 sm:tw-grid-cols-2'> <div className='tw:grid tw:grid-cols-1 tw:sm:grid-cols-2'>
{filterdItems {filterdItems
.filter((item) => { .filter((item) => {
return search === '' return search === ''
@ -110,7 +110,7 @@ export function ActionButton({
.map((i) => ( .map((i) => (
<div <div
key={i.id} key={i.id}
className='tw-cursor-pointer tw-card tw-border-[1px] tw-border-base-300 tw-card-body tw-shadow-xl tw-bg-base-100 tw-text-base-content tw-mx-4 tw-p-4 tw-mb-4 tw-h-fit' className='tw:cursor-pointer tw:card tw:border-[1px] tw:border-base-300 tw:card-body tw:shadow-xl tw:bg-base-100 tw:text-base-content tw:mx-4 tw:p-4 tw:mb-4 tw:h-fit'
onClick={() => { onClick={() => {
triggerItemSelected(i.id) triggerItemSelected(i.id)
setModalOpen(false) setModalOpen(false)

View File

@ -166,28 +166,28 @@ export const AvatarWidget: React.FC<AvatarWidgetProps> = ({ avatar, setAvatar })
<input <input
type='file' type='file'
accept='image/*' accept='image/*'
className='tw-file-input tw-w-full tw-max-w-xs' className='tw:file-input tw:w-full tw:max-w-xs'
onChange={onImageChange} onChange={onImageChange}
/> />
<div className='button tw-btn tw-btn-lg tw-btn-circle tw-animate-none'> <div className='button tw:btn tw:btn-lg tw:btn-circle tw:animate-none'>
<ArrowUpTrayIcon className='tw-w-6 tw-h-6' /> <ArrowUpTrayIcon className='tw:w-6 tw:h-6' />
</div> </div>
{avatar ? ( {avatar ? (
<div className='tw-h-20 tw-w-20'> <div className='tw:h-20 tw:w-20'>
<img <img
src={appState.assetsApi.url + avatar} src={appState.assetsApi.url + avatar}
className='tw-h-20 tw-w-20 tw-rounded-full' className='tw:h-20 tw:w-20 tw:rounded-full'
/> />
</div> </div>
) : ( ) : (
<div className='tw-h-20 tw-w-20'> <div className='tw:h-20 tw:w-20'>
<img src={UserSVG} className='tw-rounded-full'></img> <img src={UserSVG} className='tw:rounded-full'></img>
</div> </div>
)} )}
</label> </label>
) : ( ) : (
<div className='tw-w-20 tw-flex tw-items-center tw-justify-center'> <div className='tw:w-20 tw:flex tw:items-center tw:justify-center'>
<span className='tw-loading tw-loading-spinner'></span> <span className='tw:loading tw:loading-spinner'></span>
</div> </div>
)} )}
<DialogModal <DialogModal
@ -203,7 +203,7 @@ export const AvatarWidget: React.FC<AvatarWidgetProps> = ({ avatar, setAvatar })
<img src={image} ref={imgRef} onLoad={onImageLoad} /> <img src={image} ref={imgRef} onLoad={onImageLoad} />
</ReactCrop> </ReactCrop>
<button <button
className={'tw-btn tw-btn-primary'} className={'tw:btn tw:btn-primary'}
onClick={() => { onClick={() => {
setCropping(true) setCropping(true)
setCropModalOpen(false) setCropModalOpen(false)

View File

@ -38,7 +38,7 @@ export const ColorPicker = ({ color, onChange, className }) => {
<div className='swatch' style={{ backgroundColor: color }} onClick={() => toggle(true)} /> <div className='swatch' style={{ backgroundColor: color }} onClick={() => toggle(true)} />
{isOpen && ( {isOpen && (
<div className='popover tw-z-[10000]' ref={popover}> <div className='popover tw:z-10000' ref={popover}>
<HexColorPicker color={color} onChange={onChange} onClick={() => toggle(false)} /> <HexColorPicker color={color} onChange={onChange} onClick={() => toggle(false)} />
</div> </div>
)} )}

View File

@ -12,11 +12,11 @@ export const ContactInfoForm = ({
setState: React.Dispatch<React.SetStateAction<any>> setState: React.Dispatch<React.SetStateAction<any>>
}) => { }) => {
return ( return (
<div className='tw-mt-4 tw-space-y-4'> <div className='tw:mt-4 tw:space-y-4'>
<div> <div>
<label <label
htmlFor='email' htmlFor='email'
className='tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1' className='tw:block tw:text-sm tw:font-medium tw:text-gray-500 tw:mb-1'
> >
Email-Adresse (Kontakt): Email-Adresse (Kontakt):
</label> </label>
@ -37,7 +37,7 @@ export const ContactInfoForm = ({
<div> <div>
<label <label
htmlFor='telephone' htmlFor='telephone'
className='tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1' className='tw:block tw:text-sm tw:font-medium tw:text-gray-500 tw:mb-1'
> >
Telefonnummer (Kontakt): Telefonnummer (Kontakt):
</label> </label>

View File

@ -24,33 +24,33 @@ export const ContactInfoView = ({ item, heading }: { item: Item; heading: string
}, [item, items]) }, [item, items])
return ( return (
<div className='tw-bg-base-200 tw-mb-6 tw-mt-6 tw-p-6'> <div className='tw:bg-base-200 tw:mb-6 tw:mt-6 tw:p-6'>
<h2 className='tw-text-lg tw-font-semibold'>{heading}</h2> <h2 className='tw:text-lg tw:font-semibold'>{heading}</h2>
<div className='tw-mt-4 tw-flex tw-items-center'> <div className='tw:mt-4 tw:flex tw:items-center'>
{profileOwner?.image && ( {profileOwner?.image && (
<ConditionalLink url={'/item/' + profileOwner?.id}> <ConditionalLink url={'/item/' + profileOwner?.id}>
<div className='tw-mr-5 tw-flex tw-items-center tw-justify-center'> <div className='tw:mr-5 tw:flex tw:items-center tw:justify-center'>
<div className='tw-avatar'> <div className='tw:avatar'>
<div className='tw-w-20 tw-h-20 tw-bg-gray-200 rounded-full tw-flex tw-items-center tw-justify-center overflow-hidden'> <div className='tw:w-20 tw:h-20 tw:bg-gray-200 rounded-full tw:flex tw:items-center tw:justify-center overflow-hidden'>
<img <img
src={appState.assetsApi.url + profileOwner?.image} src={appState.assetsApi.url + profileOwner?.image}
alt={profileOwner?.name} alt={profileOwner?.name}
className='tw-w-full tw-h-full tw-object-cover' className='tw:w-full tw:h-full tw:object-cover'
/> />
</div> </div>
</div> </div>
</div> </div>
</ConditionalLink> </ConditionalLink>
)} )}
<div className='tw-text-sm tw-flex-grow'> <div className='tw:text-sm tw:grow'>
<p className='tw-font-semibold'>{profileOwner?.name}</p> <p className='tw:font-semibold'>{profileOwner?.name}</p>
{item.contact && ( {item.contact && (
<p> <p>
<a <a
href={`mailto:${item.contact}`} href={`mailto:${item.contact}`}
className='tw-mt-2 tw-text-green-500 tw-inline-flex tw-items-center' className='tw:mt-2 tw:text-green-500 tw:inline-flex tw:items-center'
> >
<EnvelopeIcon className='tw-w-4 tw-h-4 tw-mr-1' /> <EnvelopeIcon className='tw:w-4 tw:h-4 tw:mr-1' />
{item.contact} {item.contact}
</a> </a>
</p> </p>
@ -59,9 +59,9 @@ export const ContactInfoView = ({ item, heading }: { item: Item; heading: string
<p> <p>
<a <a
href={`tel:${item.telephone}`} href={`tel:${item.telephone}`}
className='tw-mt-2 tw-text-green-500 tw-inline-flex tw-items-center tw-whitespace-nowrap' className='tw:mt-2 tw:text-green-500 tw:inline-flex tw:items-center tw:whitespace-nowrap'
> >
<PhoneIcon className='tw-w-4 tw-h-4 tw-mr-1' /> <PhoneIcon className='tw:w-4 tw:h-4 tw:mr-1' />
{item.telephone} {item.telephone}
</a> </a>
</p> </p>

View File

@ -12,11 +12,11 @@ export const CrowdfundingForm = ({
setState: React.Dispatch<React.SetStateAction<any>> setState: React.Dispatch<React.SetStateAction<any>>
}) => { }) => {
return ( return (
<div className='tw-mt-4 tw-space-y-4'> <div className='tw:mt-4 tw:space-y-4'>
<div> <div>
<label <label
htmlFor='OpenCollectiveSlug' htmlFor='OpenCollectiveSlug'
className='tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1' className='tw:block tw:text-sm tw:font-medium tw:text-gray-500 tw:mb-1'
> >
Open Collective Slug: Open Collective Slug:
</label> </label>

View File

@ -121,18 +121,18 @@ export const CrowdfundingView = ({ item }: { item: Item }) => {
if (loading) if (loading)
return ( return (
<div className='tw-flex tw-justify-center'> <div className='tw:flex tw:justify-center'>
<span className='tw-loading tw-loading-spinner tw-loading-lg tw-text-neutral-content'></span> <span className='tw:loading tw:loading-spinner tw:loading-lg tw:text-neutral-content'></span>
</div> </div>
) )
if (error) { if (error) {
return <p className='tw-text-center tw-text-lg tw-text-red-500'>Error: {error}</p> return <p className='tw:text-center tw:text-lg tw:text-red-500'>Error: {error}</p>
} }
if (!data?.account) { if (!data?.account) {
return ( return (
<p className='tw-text-center tw-text-lg tw-text-red-500'> <p className='tw:text-center tw:text-lg tw:text-red-500'>
No data available for this account. No data available for this account.
</p> </p>
) )
@ -144,46 +144,46 @@ export const CrowdfundingView = ({ item }: { item: Item }) => {
const currentBalance = balanceValueInCents const currentBalance = balanceValueInCents
return ( return (
<div className='tw-mx-6 tw-mb-6'> <div className='tw:mx-6 tw:mb-6'>
<div className='tw-card tw-bg-base-200 tw-w-fit tw-max-w-full tw-shadow'> <div className='tw:card tw:bg-base-200 tw:w-fit tw:max-w-full tw:shadow'>
<div className='tw-stats tw-bg-base-200 tw-stats-horizontal tw-rounded-b-none'> <div className='tw:stats tw:bg-base-200 tw:stats-horizontal tw:rounded-b-none'>
<div className='tw-stat tw-p-3'> <div className='tw:stat tw:p-3'>
<div className='tw-stat-title'>Current Balance</div> <div className='tw:stat-title'>Current Balance</div>
<div className='tw-stat-value tw-text-xl lg:tw-text-3xl'> <div className='tw:stat-value tw:text-xl lg:tw:text-3xl'>
{formatCurrency(currentBalance, currency)} {formatCurrency(currentBalance, currency)}
</div> </div>
</div> </div>
<div className='tw-stat tw-p-3'> <div className='tw:stat tw:p-3'>
<div className='tw-stat-title'>Received</div> <div className='tw:stat-title'>Received</div>
<div className='tw-stat-value tw-text-green-500 tw-text-xl lg:tw-text-3xl'> <div className='tw:stat-value tw:text-green-500 tw:text-xl lg:tw:text-3xl'>
{formatCurrency(stats.totalAmountReceived.valueInCents, currency)} {formatCurrency(stats.totalAmountReceived.valueInCents, currency)}
</div> </div>
</div> </div>
<div className='tw-stat tw-p-3'> <div className='tw:stat tw:p-3'>
<div className='tw-stat-title'>Spent</div> <div className='tw:stat-title'>Spent</div>
<div className='tw-stat-value tw-text-red-500 tw-text-xl lg:tw-text-3xl'> <div className='tw:stat-value tw:text-red-500 tw:text-xl lg:tw:text-3xl'>
{formatCurrency(stats.totalAmountReceived.valueInCents - currentBalance, currency)} {formatCurrency(stats.totalAmountReceived.valueInCents - currentBalance, currency)}
</div> </div>
</div> </div>
</div> </div>
<hr></hr> <hr className='tw:border-1 tw:border-current/10 tw:border-dashed'></hr>
<div className='tw-m-4 tw-items-center'> <div className='tw:m-4 tw:items-center'>
<a href={`https://opencollective.com/${slug}/donate`} target='_blank' rel='noreferrer'> <a href={`https://opencollective.com/${slug}/donate`} target='_blank' rel='noreferrer'>
<button className='tw-btn tw-btn-sm tw-btn-primary tw-float-right tw-ml-4'> <button className='tw:btn tw:btn-sm tw:btn-primary tw:float-right tw:ml-4'>
Donate Donate
</button> </button>
</a> </a>
<div className='tw-flex-1 tw-mr-4'> <div className='tw:flex-1 tw:mr-4'>
Support{' '} Support{' '}
<a <a
className='tw-font-bold' className='tw:font-bold'
href={`https://opencollective.com/${slug}`} href={`https://opencollective.com/${slug}`}
target='_blank' target='_blank'
rel='noreferrer' rel='noreferrer'
> >
{data.account.name} {data.account.name}
</a>{' '} </a>{' '}
on <span className='tw-font-bold'>Open&nbsp;Collective</span> on <span className='tw:font-bold'>Open&nbsp;Collective</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -10,50 +10,54 @@ import { ColorPicker } from './ColorPicker'
export const FormHeader = ({ item, state, setState }) => { export const FormHeader = ({ item, state, setState }) => {
return ( return (
<div className='tw-flex'> <div className='tw:flex-none'>
<AvatarWidget <div className='tw:flex'>
avatar={state.image} <AvatarWidget
setAvatar={(i) => avatar={state.image}
setState((prevState) => ({ setAvatar={(i) =>
...prevState,
image: i,
}))
}
/>
<ColorPicker
color={state.color}
onChange={(c) =>
setState((prevState) => ({
...prevState,
color: c,
}))
}
className={'-tw-left-6 tw-top-14 -tw-mr-6'}
/>
<div className='tw-grow tw-mr-4'>
<TextInput
placeholder='Name'
defaultValue={item?.name ? item.name : ''}
updateFormValue={(v) =>
setState((prevState) => ({ setState((prevState) => ({
...prevState, ...prevState,
name: v, image: i,
})) }))
} }
containerStyle='tw-grow tw-input-md'
/> />
<TextInput <ColorPicker
placeholder='Subtitle' color={state.color}
required={false} onChange={(c) =>
defaultValue={item?.subname ? item.subname : ''}
updateFormValue={(v) =>
setState((prevState) => ({ setState((prevState) => ({
...prevState, ...prevState,
subname: v, color: c,
})) }))
} }
containerStyle='tw-grow tw-input-sm tw-px-4 tw-mt-1' className={'tw:-left-6 tw:top-14 tw:-mr-6'}
/> />
<div className='tw:grow tw:mr-4 tw:pt-1'>
<TextInput
placeholder='Name'
defaultValue={item?.name ? item.name : ''}
updateFormValue={(v) =>
setState((prevState) => ({
...prevState,
name: v,
}))
}
containerStyle='tw:grow tw:px-4'
inputStyle='tw:input-md'
/>
<TextInput
placeholder='Subtitle'
required={false}
defaultValue={item?.subname ? item.subname : ''}
updateFormValue={(v) =>
setState((prevState) => ({
...prevState,
subname: v,
}))
}
containerStyle='tw:grow tw:px-4 tw:mt-1'
inputStyle='tw:input-sm'
/>
</div>
</div> </div>
</div> </div>
) )

View File

@ -21,7 +21,7 @@ export const GalleryView = ({ item }: { item: Item }) => {
if (!images) throw new Error('GalleryView: images is undefined') if (!images) throw new Error('GalleryView: images is undefined')
return ( return (
<div className='tw-mx-6 tw-mb-6'> <div className='tw:mx-6 tw:mb-6'>
<RowsPhotoAlbum <RowsPhotoAlbum
photos={images} photos={images}
targetRowHeight={150} targetRowHeight={150}

View File

@ -11,18 +11,18 @@ export const GroupSubHeaderView = ({
shareBaseUrl: string shareBaseUrl: string
platforms?: string[] platforms?: string[]
}) => ( }) => (
<div className='tw-px-6'> <div className='tw:px-6'>
<div className='tw-float-left tw-mt-2 tw-mb-4 tw-flex tw-items-center'> <div className='tw:float-left tw:mt-2 tw:mb-4 tw:flex tw:items-center'>
{item.status && ( {item.status && (
<div className='tw-mt-1.5'> <div className='tw:mt-1.5'>
<span className='tw-text-sm tw-text-current tw-bg-base-300 tw-rounded tw-py-0.5 tw-px-2 tw-inline-flex tw-items-center tw-mr-2'> <span className='tw:text-sm tw:text-current tw:bg-base-300 tw:rounded tw:py-0.5 tw:px-2 tw:inline-flex tw:items-center tw:mr-2'>
{item.status} {item.status}
</span> </span>
</div> </div>
)} )}
{item.group_type && ( {item.group_type && (
<div className='tw-mt-1.5'> <div className='tw:mt-1.5'>
<span className='tw-text-sm tw-text-current tw-bg-base-300 tw-rounded tw-py-1 tw-px-2'> <span className='tw:text-sm tw:text-current tw:bg-base-300 tw:rounded tw:py-1 tw:px-2'>
{item.group_type} {item.group_type}
</span> </span>
</div> </div>

View File

@ -51,11 +51,11 @@ export const GroupSubheaderForm = ({
}, [state.group_type, groupTypes]) }, [state.group_type, groupTypes])
return ( return (
<div className='tw-grid tw-grid-cols-1 md:tw-grid-cols-2 tw-gap-6'> <div className='tw:grid tw:grid-cols-1 tw:md:grid-cols-2 tw:gap-6'>
<div> <div>
<label <label
htmlFor='status' htmlFor='status'
className='tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1' className='tw:block tw:text-sm tw:font-medium tw:text-gray-500 tw:mb-1'
> >
Gruppenstatus: Gruppenstatus:
</label> </label>
@ -74,7 +74,7 @@ export const GroupSubheaderForm = ({
<div> <div>
<label <label
htmlFor='groupType' htmlFor='groupType'
className='tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1' className='tw:block tw:text-sm tw:font-medium tw:text-gray-500 tw:mb-1'
> >
Gruppenart: Gruppenart:
</label> </label>

View File

@ -33,47 +33,47 @@ export function LinkedItemsHeaderView({
return ( return (
<> <>
<div className='tw-flex tw-flex-row'> <div className='tw:flex tw:flex-row'>
<div className={'tw-grow tw-max-w-[calc(100%-60px)] }'}> <div className={'tw:grow tw:max-w-[calc(100%-60px)] }'}>
<div className='flex items-center'> <div className='flex items-center'>
{avatar && ( {avatar && (
<img <img
className={'tw-w-10 tw-inline tw-rounded-full'} className={'tw:w-10 tw:inline tw:rounded-full'}
src={avatar} src={avatar}
alt={item.name + ' logo'} alt={item.name + ' logo'}
/> />
)} )}
<div className={`${avatar ? 'tw-ml-2' : ''} tw-overflow-hidden`}> <div className={`${avatar ? 'tw:ml-2' : ''} tw:overflow-hidden`}>
<div className={'tw-text-xl tw-font-semibold tw-truncate'}>{title}</div> <div className={'tw:text-xl tw:font-semibold tw:truncate'}>{title}</div>
{subtitle && ( {subtitle && (
<div className='tw-text-xs tw-truncate tw-text-gray-500 '>{subtitle}</div> <div className='tw:text-xs tw:truncate tw:text-gray-500 '>{subtitle}</div>
)} )}
</div> </div>
</div> </div>
</div> </div>
<div className='tw-col-span-1' onClick={(e) => e.stopPropagation()}> <div className='tw:col-span-1' onClick={(e) => e.stopPropagation()}>
{unlinkPermission && ( {unlinkPermission && (
<div className='tw-dropdown tw-dropdown-bottom'> <div className='tw:dropdown tw:dropdown-bottom'>
<label <label
tabIndex={0} tabIndex={0}
className=' tw-btn tw-m-1 tw-leading-3 tw-border-none tw-min-h-0 tw-h-6' className=' tw:btn tw:m-1 tw:leading-3 tw:border-none tw:min-h-0 tw:h-6'
> >
<EllipsisVerticalIcon className='tw-h-5 tw-w-5' /> <EllipsisVerticalIcon className='tw:h-5 tw:w-5' />
</label> </label>
<ul <ul
tabIndex={0} tabIndex={0}
className='tw-dropdown-content tw-menu tw-p-2 tw-shadow tw-bg-base-100 tw-rounded-box tw-z-1000' className='tw:dropdown-content tw:menu tw:p-2 tw:shadow tw:bg-base-100 tw:rounded-box tw:z-1000'
> >
{true && ( {true && (
<li> <li>
<a <a
className='tw-cursor-pointer !tw-text-error' className='tw:cursor-pointer tw:text-error!'
onClick={() => unlinkCallback(item.id)} onClick={() => unlinkCallback(item.id)}
> >
{loading ? ( {loading ? (
<span className='tw-loading tw-loading-spinner tw-loading-sm'></span> <span className='tw:loading tw:loading-spinner tw:loading-sm'></span>
) : ( ) : (
<LinkSlashIcon className='tw-h-5 tw-w-5 tw-stroke-[3]' /> <LinkSlashIcon className='tw:h-5 tw:w-5 tw:stroke-3' />
)} )}
</a> </a>
</li> </li>

View File

@ -8,16 +8,16 @@ export const MarkdownHint = () => {
<div <div
onClick={() => setExpended(true)} onClick={() => setExpended(true)}
title='Markdown is supported' title='Markdown is supported'
className='flex tw-flex-row tw-text-gray-400 tw-cursor-pointer tw-items-center' className='flex tw:flex-row tw:text-gray-400 tw:cursor-pointer tw:items-center'
> >
<img src={MarkdownSVG} alt='Markdown' className='octicon octicon-markdown tw-gray-400' /> <img src={MarkdownSVG} alt='Markdown' className='octicon octicon-markdown tw:gray-400' />
{expended && ( {expended && (
<a <a
href='https://www.markdownguide.org/cheat-sheet/#basic-syntax' href='https://www.markdownguide.org/cheat-sheet/#basic-syntax'
target='_blank' target='_blank'
rel='noreferrer' rel='noreferrer'
> >
<span className='Button-label tw-ml-1'>Markdown is support</span>{' '} <span className='Button-label tw:ml-1'>Markdown is support</span>{' '}
</a> </a>
)} )}
</div> </div>

View File

@ -21,16 +21,16 @@ export function PlusButton({
return ( return (
<> <>
{hasUserPermission(collection, 'create', undefined, layer) && ( {hasUserPermission(collection, 'create', undefined, layer) && (
<div className='tw-dropdown tw-dropdown-top tw-dropdown-end tw-dropdown-hover tw-z-3000 tw-absolute tw-right-4 tw-bottom-4'> <div className='tw:dropdown tw:dropdown-top tw:dropdown-end tw:dropdown-hover tw:z-3000 tw:absolute tw:right-4 tw:bottom-4'>
<button <button
tabIndex={0} tabIndex={0}
className='tw-z-500 tw-btn tw-btn-circle tw-shadow' className='tw:z-500 tw:btn tw:btn-circle tw:shadow'
onClick={() => { onClick={() => {
triggerAction() triggerAction()
}} }}
style={{ backgroundColor: color, color: '#fff' }} style={{ backgroundColor: color, color: '#fff' }}
> >
<PlusIcon className='tw-w-5 tw-h-5 tw-stroke-[2.5]' /> <PlusIcon className='tw:w-5 tw:h-5 tw:stroke-[2.5]' />
</button> </button>
</div> </div>
)} )}

View File

@ -4,7 +4,7 @@ import type { Item } from '#types/Item'
export const ProfileStartEndView = ({ item }: { item: Item }) => { export const ProfileStartEndView = ({ item }: { item: Item }) => {
return ( return (
<div className='tw-mt-2 tw-px-6 tw-max-w-xs'> <div className='tw:mt-2 tw:px-6 tw:max-w-xs'>
<StartEndView item={item}></StartEndView> <StartEndView item={item}></StartEndView>
</div> </div>
) )

View File

@ -37,11 +37,11 @@ export const ProfileTextForm = ({
}, [dataField]) }, [dataField])
return ( return (
<div className='tw-h-full tw-flex tw-flex-col tw-mt-4'> <div className='tw:h-full tw:flex tw:flex-col tw:mt-4'>
<div className='tw-flex tw-justify-between tw-items-center'> <div className='tw:flex tw:justify-between tw:items-center'>
<label <label
htmlFor='nextAppointment' htmlFor='nextAppointment'
className='tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1' className='tw:block tw:text-sm tw:font-medium tw:text-gray-500 tw:mb-1'
> >
{heading || 'Text'}: {heading || 'Text'}:
</label> </label>
@ -57,9 +57,9 @@ export const ProfileTextForm = ({
[field]: v, [field]: v,
})) }))
} }
labelStyle={hideInputLabel ? 'tw-hidden' : ''} labelStyle={hideInputLabel ? 'tw:hidden' : ''}
containerStyle={size === 'full' ? 'tw-grow tw-h-full' : ''} containerStyle={size === 'full' ? 'tw:grow tw:h-full' : ''}
inputStyle={size === 'full' ? 'tw-h-full' : 'tw-h-24'} inputStyle={size === 'full' ? 'tw:h-full' : 'tw:h-24'}
required={required} required={required}
/> />
</div> </div>

View File

@ -20,11 +20,11 @@ export const ProfileTextView = ({
const parsedText = typeof text !== 'string' ? '' : text const parsedText = typeof text !== 'string' ? '' : text
return ( return (
<div className='tw-my-10 tw-mt-2 tw-px-6'> <div className='tw:my-10 tw:mt-2 tw:px-6'>
{!(text === '' && hideWhenEmpty) && ( {!(text === '' && hideWhenEmpty) && (
<h2 className='tw-text-lg tw-font-semibold'>{heading}</h2> <h2 className='tw:text-lg tw:font-semibold'>{heading}</h2>
)} )}
<div className='tw-mt-2 tw-text-sm'> <div className='tw:mt-2 tw:text-sm'>
<TextView itemId={item.id} rawText={parsedText} /> <TextView itemId={item.id} rawText={parsedText} />
</div> </div>
</div> </div>

View File

@ -1,15 +1,15 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
// eslint-disable-next-line react/prop-types // eslint-disable-next-line react/prop-types
const RelationCard = ({ title, description, imageSrc }) => ( const RelationCard = ({ title, description, imageSrc }) => (
<div className={`tw-mb-6 ${imageSrc ? 'md:tw-flex md:tw-space-x-4' : ''}`}> <div className={`tw:mb-6 ${imageSrc ? 'tw:md:flex tw:md:space-x-4' : ''}`}>
{imageSrc && ( {imageSrc && (
<div className='md:tw-w-1/2 tw-mb-4 md:tw-mb-0'> <div className='tw:md:w-1/2 tw:mb-4 tw:md:mb-0'>
<img src={imageSrc} alt={title} className='tw-w-full tw-h-32 tw-object-cover' /> <img src={imageSrc} alt={title} className='tw:w-full tw:h-32 tw:object-cover' />
</div> </div>
)} )}
<div className={imageSrc ? 'md:tw-w-1/2' : 'tw-w-full'}> <div className={imageSrc ? 'tw:md:w-1/2' : 'tw:w-full'}>
<h3 className='tw-text-lg tw-font-semibold'>{title}</h3> <h3 className='tw:text-lg tw:font-semibold'>{title}</h3>
<p className='tw-mt-2 tw-text-sm tw-text-gray-600'>{description}</p> <p className='tw:mt-2 tw:text-sm tw:text-gray-600'>{description}</p>
</div> </div>
</div> </div>
) )

View File

@ -27,7 +27,7 @@ const SocialShareBar = ({
}) })
} }
return ( return (
<div className='tw-flex tw-place-content-end tw-justify-end tw-space-x-2 tw-grow tw-min-w-fit tw-pl-2'> <div className='tw:flex tw:place-content-end tw:justify-end tw:space-x-2 tw:grow tw:min-w-fit tw:pl-2'>
{platforms.map((platform) => ( {platforms.map((platform) => (
<SocialShareButton key={platform} platform={platform} url={url} title={title} /> <SocialShareButton key={platform} platform={platform} url={url} title={title} />
))} ))}
@ -36,7 +36,7 @@ const SocialShareBar = ({
href={`mailto:?subject=${title}&body=${url}`} href={`mailto:?subject=${title}&body=${url}`}
target='_blank' target='_blank'
rel='noopener noreferrer' rel='noopener noreferrer'
className='tw-w-8 tw-h-8 tw-mt-2 tw-rounded-full tw-flex tw-items-center tw-justify-center tw-text-white hover:tw-cursor-pointer' className='tw:w-8 tw:h-8 tw:mt-2 tw:rounded-full tw:flex tw:items-center tw:justify-center tw:text-white tw:hover:cursor-pointer'
style={{ style={{
color: 'white', color: 'white',
backgroundColor: '#444', backgroundColor: '#444',
@ -44,13 +44,13 @@ const SocialShareBar = ({
onClick={() => copyLink()} onClick={() => copyLink()}
title='share link via email' title='share link via email'
> >
<img src={ChevronSVG} alt='\/' className='tw-h-4 tw-w-4' /> <img src={ChevronSVG} alt='\/' className='tw:h-4 tw:w-4' />
</a> </a>
)} )}
{platforms.includes('clipboard') && ( {platforms.includes('clipboard') && (
<div <div
rel='noopener noreferrer' rel='noopener noreferrer'
className='tw-w-8 tw-h-8 tw-mt-2 tw-rounded-full tw-flex tw-items-center tw-justify-center tw-text-white hover:tw-cursor-pointer' className='tw:w-8 tw:h-8 tw:mt-2 tw:rounded-full tw:flex tw:items-center tw:justify-center tw:text-white tw:hover:cursor-pointer'
style={{ style={{
color: 'white', color: 'white',
backgroundColor: '#888', backgroundColor: '#888',
@ -58,7 +58,7 @@ const SocialShareBar = ({
onClick={() => copyLink()} onClick={() => copyLink()}
title='copy Link' title='copy Link'
> >
<img src={ClipboardSVG} className='tw-w-5' /> <img src={ClipboardSVG} className='tw:w-5' />
</div> </div>
)} )}
</div> </div>

View File

@ -71,14 +71,14 @@ const SocialShareButton = ({
href={finalShareUrl} href={finalShareUrl}
target='_blank' target='_blank'
rel='noopener noreferrer' rel='noopener noreferrer'
className='tw-w-8 tw-h-8 tw-mt-2 tw-rounded-full tw-flex tw-items-center tw-justify-center tw-text-white' className='tw:w-8 tw:h-8 tw:mt-2 tw:rounded-full tw:flex tw:items-center tw:justify-center tw:text-white'
style={{ style={{
color: 'white', color: 'white',
backgroundColor: bgColor, backgroundColor: bgColor,
}} }}
title={`share link on ${platform}`} title={`share link on ${platform}`}
> >
{cloneElement(icon, { className: 'tw-w-4 tw-h-4 tw-fill-current' })} {cloneElement(icon, { className: 'tw:w-4 tw:h-4 tw:fill-current' })}
</a> </a>
) )
} }

View File

@ -95,7 +95,7 @@ export const TagsWidget = ({ placeholder, containerStyle, defaultTags, onUpdate
onKeyDown, onKeyDown,
onKeyUp, onKeyUp,
onChange, onChange,
className: 'tw-bg-transparent tw-w-fit tw-mt-5 tw-h-fit', className: 'tw:bg-transparent tw:w-fit tw:mt-5 tw:h-fit',
} }
/* eslint-disable react/prop-types */ /* eslint-disable react/prop-types */
@ -107,18 +107,18 @@ export const TagsWidget = ({ placeholder, containerStyle, defaultTags, onUpdate
setFocusInput(false) setFocusInput(false)
}, 200) }, 200)
}} }}
className={`tw-input tw-input-bordered tw-cursor-text ${containerStyle}`} className={`tw:textarea tw:cursor-text ${containerStyle}`}
> >
<div className='tw-flex tw-flex-wrap tw-h-fit'> <div className='tw:flex tw:flex-wrap tw:h-fit'>
{defaultTags.map((tag) => ( {defaultTags.map((tag) => (
<div <div
key={tag.name} key={tag.name}
className='tw-rounded-2xl tw-text-white tw-p-2 tw-px-4 tw-shadow-xl tw-card tw-mt-3 tw-mr-4' className='tw:rounded-2xl tw:text-white tw:p-2 tw:px-4 tw:shadow-xl tw:card tw:mt-3 tw:mr-4'
style={{ backgroundColor: tag.color ? tag.color : '#666' }} style={{ backgroundColor: tag.color ? tag.color : '#666' }}
> >
<div className='tw-card-actions tw-justify-end'> <div className='tw:card-actions tw:justify-end'>
<label <label
className='tw-btn tw-btn-xs tw-btn-circle tw-absolute tw--right-2 tw--top-2 tw-bg-white tw-text-gray-600' className='tw:btn tw:btn-xs tw:btn-circle tw:absolute tw:-right-2 tw:-top-2 tw:bg-white tw:text-gray-600'
onClick={() => deleteTag(tag)} onClick={() => deleteTag(tag)}
> >

View File

@ -29,7 +29,7 @@ export const FlexForm = ({
item: Item item: Item
}) => { }) => {
return ( return (
<div className='tw-mt-6 tw-flex tw-flex-col tw-h-full'> <div className='tw:mt-6 tw:flex tw:flex-col tw:h-full'>
{item.layer?.itemType.profileTemplate.map((templateItem) => { {item.layer?.itemType.profileTemplate.map((templateItem) => {
const TemplateComponent = componentMap[templateItem.collection] const TemplateComponent = componentMap[templateItem.collection]
return TemplateComponent ? ( return TemplateComponent ? (
@ -41,7 +41,9 @@ export const FlexForm = ({
{...templateItem.item} {...templateItem.item}
/> />
) : ( ) : (
<div key={templateItem.id}>Component not found</div> <div className='tw:mt-2' key={templateItem.id}>
{templateItem.collection} form not found
</div>
) )
})} })}
</div> </div>

View File

@ -21,10 +21,8 @@ const componentMap = {
} }
export const FlexView = ({ item }: { item: Item }) => { export const FlexView = ({ item }: { item: Item }) => {
// eslint-disable-next-line no-console
console.log(item)
return ( return (
<div className='tw-h-full tw-overflow-y-auto fade'> <div className='tw:h-full tw:overflow-y-auto fade'>
{item.layer?.itemType.profileTemplate.map( {item.layer?.itemType.profileTemplate.map(
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
(templateItem: { collection: string | number; id: Key | null | undefined; item: any }) => { (templateItem: { collection: string | number; id: Key | null | undefined; item: any }) => {
@ -32,7 +30,9 @@ export const FlexView = ({ item }: { item: Item }) => {
return TemplateComponent ? ( return TemplateComponent ? (
<TemplateComponent key={templateItem.id} item={item} {...templateItem.item} /> <TemplateComponent key={templateItem.id} item={item} {...templateItem.item} />
) : ( ) : (
<div key={templateItem.id}>Component not found</div> <div className='tw:mx-6 tw:mb-6' key={templateItem.id}>
{templateItem.collection} view not found
</div>
) )
}, },
)} )}

View File

@ -17,14 +17,14 @@ export const OnepagerForm = ({
item: Item item: Item
}) => { }) => {
return ( return (
<div className='tw-space-y-6 tw-mt-6'> <div className='tw:space-y-6 tw:mt-6'>
<GroupSubheaderForm state={state} setState={setState} item={item}></GroupSubheaderForm> <GroupSubheaderForm state={state} setState={setState} item={item}></GroupSubheaderForm>
<ContactInfoForm state={state} setState={setState}></ContactInfoForm> <ContactInfoForm state={state} setState={setState}></ContactInfoForm>
<div> <div>
<label <label
htmlFor='description' htmlFor='description'
className='tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1' className='tw:block tw:text-sm tw:font-medium tw:text-gray-500 tw:mb-1'
> >
Gruppenbeschreibung: Gruppenbeschreibung:
</label> </label>
@ -37,7 +37,7 @@ export const OnepagerForm = ({
text: v, text: v,
})) }))
} }
inputStyle='tw-h-48' inputStyle='tw:h-48'
/> />
</div> </div>
</div> </div>

View File

@ -7,29 +7,29 @@ import type { Item } from '#types/Item'
export const OnepagerView = ({ item }: { item: Item }) => { export const OnepagerView = ({ item }: { item: Item }) => {
return ( return (
<div className='tw-h-full tw-overflow-y-auto fade'> <div className='tw:h-full tw:overflow-y-auto fade'>
<GroupSubHeaderView <GroupSubHeaderView
item={item} item={item}
shareBaseUrl={`https://www.wuerdekompass.org/aktivitaeten/gruppensuche/#/gruppe/${item.slug}`} shareBaseUrl={`https://www.wuerdekompass.org/aktivitaeten/gruppensuche/#/gruppe/${item.slug}`}
/> />
{item.user_created?.first_name && <ContactInfoView heading='Du hast Fragen?' item={item} />} {item.user_created?.first_name && <ContactInfoView heading='Du hast Fragen?' item={item} />}
{/* Description Section */} {/* Description Section */}
<div className='tw-my-10 tw-mt-2 tw-px-6 tw-text-sm '> <div className='tw:my-10 tw:mt-2 tw:px-6 tw:text-sm '>
<TextView itemId={item.id} rawText={item.text ?? 'Keine Beschreibung vorhanden'} /> <TextView itemId={item.id} rawText={item.text ?? 'Keine Beschreibung vorhanden'} />
</div> </div>
{/* Next Appointment Section */} {/* Next Appointment Section */}
{item.next_appointment && ( {item.next_appointment && (
<div className='tw-my-10 tw-px-6'> <div className='tw:my-10 tw:px-6'>
<h2 className='tw-text-lg tw-font-semibold'>Nächste Termine</h2> <h2 className='tw:text-lg tw:font-semibold'>Nächste Termine</h2>
<div className='tw-mt-2 tw-text-sm'> <div className='tw:mt-2 tw:text-sm'>
<TextView itemId={item.id} rawText={item.next_appointment} /> <TextView itemId={item.id} rawText={item.next_appointment} />
</div> </div>
</div> </div>
)} )}
;{/* Relations Section */} ;{/* Relations Section */}
{/* {d.relations && ( */} {/* {d.relations && ( */}
{/* <div className="tw-my-10 tw-px-6"> */} {/* <div className="tw:my-10 tw:px-6"> */}
{/* <h2 className="tw-text-lg tw-font-semibold tw-mb-4">Projekte</h2> */} {/* <h2 className="tw:text-lg tw:font-semibold tw:mb-4">Projekte</h2> */}
{/* {d.relations.map((project, index) => ( */} {/* {d.relations.map((project, index) => ( */}
{/* <RelationCard */} {/* <RelationCard */}
{/* key={index} */} {/* key={index} */}

View File

@ -17,8 +17,8 @@ export const SimpleForm = ({ state, setState }) => {
text: v, text: v,
})) }))
} }
containerStyle='tw-mt-8 tw-h-full' containerStyle='tw:mt-8 tw:h-full'
inputStyle='tw-h-full' inputStyle='tw:h-full'
/> />
) )
} }

View File

@ -4,7 +4,7 @@ import type { Item } from '#types/Item'
export const SimpleView = ({ item }: { item: Item }) => { export const SimpleView = ({ item }: { item: Item }) => {
return ( return (
<div className='tw-mt-8 tw-h-full tw-overflow-y-auto fade tw-px-6'> <div className='tw:mt-8 tw:h-full tw:overflow-y-auto fade tw:px-6'>
<TextView text={item.text} itemId={item.id} /> <TextView text={item.text} itemId={item.id} />
</div> </div>
) )

View File

@ -53,55 +53,55 @@ export const TabsForm = ({
}, [location.search]) }, [location.search])
return ( return (
<div role='tablist' className='tw-tabs tw-tabs-lifted tw-mt-3'> <div className='tw:grow'>
<input <div role='tablist' className='tw:tabs tw:h-full tw:tabs-lift tw:mt-3'>
type='radio' <input
name='my_tabs_2' type='radio'
role='tab' name='my_tabs_2'
className={'tw-tab [--tab-border-color:var(--fallback-bc,oklch(var(--bc)/0.2))]'} role='tab'
aria-label='Info' className={'tw:tab '}
checked={activeTab === 1 && true} aria-label='Info'
onChange={() => updateActiveTab(1)} checked={activeTab === 1 && true}
/> onChange={() => updateActiveTab(1)}
<div />
role='tabpanel'
className='tw-tab-content tw-bg-base-100 tw-border-[var(--fallback-bc,oklch(var(--bc)/0.2))] tw-rounded-box tw-h-[calc(100dvh-332px)] tw-min-h-56 tw-border-none'
>
<div <div
className={`tw-flex tw-flex-col tw-h-full ${item.layer.itemType.show_start_end_input && 'tw-pt-4'}`} role='tabpanel'
className='tw:tab-content tw:bg-base-100 tw:border-(--fallback-bc,oklch(var(--bc)/0.2)) tw:rounded-box tw:!h-[calc(100%-48px)] tw:min-h-56 tw:border-none'
> >
{item.layer.itemType.show_start_end_input && ( <div
<PopupStartEndInput className={`tw:flex tw:flex-col tw:h-full ${item.layer.itemType.show_start_end_input && 'tw:pt-4'}`}
item={item} >
showLabels={false} {item.layer.itemType.show_start_end_input && (
updateEndValue={(e) => <PopupStartEndInput
setState((prevState) => ({ item={item}
...prevState, showLabels={false}
end: e, updateEndValue={(e) =>
})) setState((prevState) => ({
} ...prevState,
updateStartValue={(s) => end: e,
setState((prevState) => ({ }))
...prevState, }
start: s, updateStartValue={(s) =>
})) setState((prevState) => ({
} ...prevState,
></PopupStartEndInput> start: s,
)} }))
}
></PopupStartEndInput>
)}
<TextAreaInput <TextAreaInput
placeholder='about ...' placeholder='about ...'
defaultValue={item?.text ? item.text : ''} defaultValue={item?.text ? item.text : ''}
updateFormValue={(v) => updateFormValue={(v) =>
setState((prevState) => ({ setState((prevState) => ({
...prevState, ...prevState,
text: v, text: v,
})) }))
} }
containerStyle='tw-grow' containerStyle='tw:grow'
inputStyle={`tw-h-full ${!item.layer.itemType.show_start_end_input && 'tw-border-t-0 tw-rounded-tl-none'}`} inputStyle={`tw:h-full ${!item.layer.itemType.show_start_end_input && 'tw:border-t-0 tw:rounded-tl-none'}`}
/> />
<div>
<TextAreaInput <TextAreaInput
placeholder='contact info ...' placeholder='contact info ...'
defaultValue={state.contact || ''} defaultValue={state.contact || ''}
@ -111,110 +111,108 @@ export const TabsForm = ({
contact: c, contact: c,
})) }))
} }
inputStyle='tw-h-24' inputStyle=''
containerStyle='tw-pt-4' containerStyle='tw:pt-4 tw:h-24 tw:flex-none'
required={false} required={false}
/> />
</div> </div>
</div> </div>
</div> {item.layer?.itemType.offers_and_needs && (
{item.layer?.itemType.offers_and_needs && ( <>
<> <input
<input type='radio'
type='radio' name='my_tabs_2'
name='my_tabs_2' role='tab'
role='tab' className={'tw:tab tw:min-w-[10em] '}
className={ aria-label='Offers & Needs'
'tw-tab tw-min-w-[10em] [--tab-border-color:var(--fallback-bc,oklch(var(--bc)/0.2))]' checked={activeTab === 3 && true}
} onChange={() => updateActiveTab(3)}
aria-label='Offers & Needs' />
checked={activeTab === 3 && true} <div
onChange={() => updateActiveTab(3)} role='tabpanel'
/> className='tw:tab-content tw:bg-base-100 tw:border-(--fallback-bc,oklch(var(--bc)/0.2)) tw:rounded-box tw:!h-[calc(100%-48px)] tw:min-h-56 tw:border-none'
<div >
role='tabpanel' <div className='tw:h-full'>
className='tw-tab-content tw-bg-base-100 tw-border-[var(--fallback-bc,oklch(var(--bc)/0.2))] tw-rounded-box tw-h-[calc(100dvh-332px)] tw-min-h-56 tw-border-none' <div className='tw:w-full tw:h-[calc(50%-0.75em)] tw:mb-4'>
> <TagsWidget
<div className='tw-h-full'> defaultTags={state.offers}
<div className='tw-w-full tw-h-[calc(50%-0.75em)] tw-mb-4'> onUpdate={(v) =>
<TagsWidget setState((prevState) => ({
defaultTags={state.offers} ...prevState,
onUpdate={(v) => offers: v,
setState((prevState) => ({ }))
...prevState, }
offers: v, placeholder='enter your offers'
})) containerStyle='tw:bg-transparent tw:w-full tw:h-full tw:mt-3 tw:text-xs tw:h-[calc(100%-1rem)] tw:min-h-[5em] tw:pb-2 tw:overflow-auto'
} />
placeholder='enter your offers' </div>
containerStyle='tw-bg-transparent tw-w-full tw-h-full tw-mt-3 tw-text-xs tw-h-[calc(100%-1rem)] tw-min-h-[5em] tw-pb-2 tw-overflow-auto' <div className='tw:w-full tw:h-[calc(50%-1.5em)]'>
/> <TagsWidget
</div> defaultTags={state.needs}
<div className='tw-w-full tw-h-[calc(50%-1.5em)]'> onUpdate={(v) =>
<TagsWidget setState((prevState) => ({
defaultTags={state.needs} ...prevState,
onUpdate={(v) => needs: v,
setState((prevState) => ({ }))
...prevState, }
needs: v, placeholder='enter your needs'
})) containerStyle='tw:bg-transparent tw:w-full tw:h-full tw:mt-3 tw:text-xs tw:h-[calc(100%-1rem)] tw:min-h-[5em] tw:pb-2 tw:overflow-auto'
} />
placeholder='enter your needs' </div>
containerStyle='tw-bg-transparent tw-w-full tw-h-full tw-mt-3 tw-text-xs tw-h-[calc(100%-1rem)] tw-min-h-[5em] tw-pb-2 tw-overflow-auto'
/>
</div> </div>
</div> </div>
</div> </>
</> )}
)} {item.layer?.itemType.relations && (
{item.layer?.itemType.relations && ( <>
<> <input
<input type='radio'
type='radio' name='my_tabs_2'
name='my_tabs_2' role='tab'
role='tab' className='tw:tab '
className='tw-tab [--tab-border-color:var(--fallback-bc,oklch(var(--bc)/0.2))]' aria-label='Links'
aria-label='Links' checked={activeTab === 7 && true}
checked={activeTab === 7 && true} onChange={() => updateActiveTab(7)}
onChange={() => updateActiveTab(7)} />
/> <div
<div role='tabpanel'
role='tabpanel' className='tw:tab-content tw:rounded-box tw:!h-[calc(100%-48px)] tw:overflow-y-auto tw:pt-4 tw:overflow-x-hidden fade'
className='tw-tab-content tw-bg-base-100 tw-rounded-box tw-h-[calc(100dvh-332px)] tw-overflow-y-auto tw-pt-4 tw-pb-1 -tw-mx-4 tw-overflow-x-hidden fade' >
> <div className='tw:h-full'>
<div className='tw-h-full'> <div className='tw:grid tw:grid-cols-1 tw:sm:grid-cols-2 tw:md:grid-cols-1 tw:lg:grid-cols-1 tw:xl:grid-cols-1 tw:2xl:grid-cols-2 tw:mb-4'>
<div className='tw-grid tw-grid-cols-1 sm:tw-grid-cols-2 md:tw-grid-cols-1 lg:tw-grid-cols-1 xl:tw-grid-cols-1 2xl:tw-grid-cols-2 tw-mb-4'> {state.relations &&
{state.relations && state.relations.map((i) => (
state.relations.map((i) => ( <div
<div key={i.id}
key={i.id} className='tw:cursor-pointer tw:card tw:bg-base-200 tw:border-[1px] tw:border-base-300 tw:card-body tw:shadow-xl tw:text-base-content tw:mx-4 tw:p-6 tw:mb-4'
className='tw-cursor-pointer tw-card tw-bg-base-200 tw-border-[1px] tw-border-base-300 tw-card-body tw-shadow-xl tw-text-base-content tw-mx-4 tw-p-6 tw-mb-4' onClick={() => navigate('/item/' + i.id)}
onClick={() => navigate('/item/' + i.id)} >
> <LinkedItemsHeaderView
<LinkedItemsHeaderView unlinkPermission={updatePermission}
unlinkPermission={updatePermission} item={i}
item={i} unlinkCallback={(id) => unlinkItem(id, item, updateItem)}
unlinkCallback={(id) => unlinkItem(id, item, updateItem)} loading={loading}
loading={loading} />
/> <div className='tw:overflow-y-auto tw:overflow-x-hidden tw:max-h-64 fade'>
<div className='tw-overflow-y-auto tw-overflow-x-hidden tw-max-h-64 fade'> <TextView truncate itemId={item.id} />
<TextView truncate itemId={item.id} /> </div>
</div> </div>
</div> ))}
))} {updatePermission && (
{updatePermission && ( <ActionButton
<ActionButton customStyle='tw:bottom-24!'
customStyle='!tw-bottom-24' collection='items'
collection='items' item={item}
item={item} existingRelations={state.relations}
existingRelations={state.relations} triggerItemSelected={(id) => linkItem(id, item, updateItem)}
triggerItemSelected={(id) => linkItem(id, item, updateItem)} ></ActionButton>
></ActionButton> )}
)} </div>
</div> </div>
</div> </div>
</div> </>
</> )}
)} </div>
</div> </div>
) )
} }

View File

@ -85,29 +85,27 @@ export const TabsView = ({
}, [location.search]) }, [location.search])
return ( return (
<div role='tablist' className='tw-tabs tw-tabs-lifted tw-mt-2 tw-mb-2 tw-px-6'> <div role='tablist' className='tw:tabs tw:tabs-lift tw:mt-2 tw:mb-2 tw:px-6'>
<input <input
type='radio' type='radio'
name='my_tabs_2' name='my_tabs_2'
role='tab' role='tab'
className={ className={'tw:tab tw:font-bold tw:ps-2! tw:pe-2! '}
'tw-tab tw-font-bold !tw-ps-2 !tw-pe-2 [--tab-border-color:var(--fallback-bc,oklch(var(--bc)/0.2))]'
}
aria-label={`${item.layer?.itemType.icon_as_labels && activeTab !== 1 ? '📝' : '📝\u00A0Info'}`} aria-label={`${item.layer?.itemType.icon_as_labels && activeTab !== 1 ? '📝' : '📝\u00A0Info'}`}
checked={activeTab === 1 && true} checked={activeTab === 1 && true}
onChange={() => updateActiveTab(1)} onChange={() => updateActiveTab(1)}
/> />
<div <div
role='tabpanel' role='tabpanel'
className='tw-tab-content tw-bg-base-100 tw-rounded-box tw-h-[calc(100dvh-280px)] tw-overflow-y-auto fade tw-pt-2 tw-pb-4 tw-mb-4 tw-overflow-x-hidden' className='tw:tab-content tw:bg-base-100 tw:rounded-box tw:!h-[calc(100dvh-280px)] tw:overflow-y-auto fade tw:pt-2 tw:pb-4 tw:mb-4 tw:overflow-x-hidden'
> >
{item.layer?.itemType.show_start_end && ( {item.layer?.itemType.show_start_end && (
<div className='tw-max-w-xs'> <div className='tw:max-w-xs'>
<StartEndView item={item}></StartEndView> <StartEndView item={item}></StartEndView>
</div> </div>
)} )}
<TextView text={item.text} itemId={item.id} /> <TextView text={item.text} itemId={item.id} />
<div className='tw-h-4'></div> <div className='tw:h-4'></div>
<TextView text={item.contact} itemId={item.id} /> <TextView text={item.contact} itemId={item.id} />
</div> </div>
{item.layer?.itemType.questlog && ( {item.layer?.itemType.questlog && (
@ -116,18 +114,16 @@ export const TabsView = ({
type='radio' type='radio'
name='my_tabs_2' name='my_tabs_2'
role='tab' role='tab'
className={ className={'tw:tab tw:font-bold tw:ps-2! tw:pe-2!'}
'tw-tab tw-font-bold !tw-ps-2 !tw-pe-2 [--tab-border-color:var(--fallback-bc,oklch(var(--bc)/0.2))]'
}
aria-label={`${item.layer.itemType.icon_as_labels && activeTab !== 2 ? '❤️' : '❤️\u00A0Trust'}`} aria-label={`${item.layer.itemType.icon_as_labels && activeTab !== 2 ? '❤️' : '❤️\u00A0Trust'}`}
checked={activeTab === 2 && true} checked={activeTab === 2 && true}
onChange={() => updateActiveTab(2)} onChange={() => updateActiveTab(2)}
/> />
<div <div
role='tabpanel' role='tabpanel'
className='tw-tab-content tw-bg-base-100 tw-rounded-box tw-h-[calc(100dvh-280px)] tw-overflow-y-auto fade tw-pt-2 tw-pb-4 tw-mb-4 tw-overflow-x-hidden' className='tw:tab-content tw:bg-base-100 tw:rounded-box tw:!h-[calc(100dvh-280px)] tw:overflow-y-auto fade tw:pt-2 tw:pb-4 tw:mb-4 tw:overflow-x-hidden'
> >
<table className='sm:tw-table-sm md:tw-table-md'> <table className='sm:tw:table-sm md:tw:table-md tw:w-full'>
<tbody> <tbody>
{attestations {attestations
.filter((a) => a.to.some((t) => t.directus_users_id === item.user_created?.id)) .filter((a) => a.to.some((t) => t.directus_users_id === item.user_created?.id))
@ -139,13 +135,15 @@ export const TabsView = ({
<tr key={i}> <tr key={i}>
<td> <td>
<div <div
className={`tw-cursor-pointer tw-text-3xl tw-mask tw-mask-${a.shape} tw-p-3 tw-mr-2 tw-shadow-xl tw-bg-[${a.color}]`} className={`tw:cursor-pointer tw:text-3xl tw:mask ${a.shape === 'squircle' ? 'tw:mask-squircle' : a.shape === 'circle' ? 'tw:mask-circle' : 'tw:mask-hexagon-2'} tw:p-2 tw:my-2 tw:mr-2 tw:shadow-xl`}
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
style={{ backgroundColor: a.color }}
> >
{a.emoji} {a.emoji}
</div> </div>
</td> </td>
<td> <td>
<div className='tw-mr-2'> <div className='tw:mr-2'>
<i>{a.text}</i> <i>{a.text}</i>
</div> </div>
</td> </td>
@ -153,8 +151,8 @@ export const TabsView = ({
{getUserProfile(a.user_created.id) ? ( {getUserProfile(a.user_created.id) ? (
<Link to={'/item/' + getUserProfile(a.user_created.id)?.id}> <Link to={'/item/' + getUserProfile(a.user_created.id)?.id}>
<div className='flex items-center gap-3'> <div className='flex items-center gap-3'>
<div className='tw-avatar'> <div className='tw:avatar'>
<div className='tw-mask tw-rounded-full tw-h-8 tw-w-8 tw-mr-2'> <div className='tw:mask tw:rounded-full tw:h-8 tw:w-8 tw:mr-2'>
{getUserProfile(a.user_created.id)?.image && ( {getUserProfile(a.user_created.id)?.image && (
<img <img
src={ src={
@ -171,7 +169,7 @@ export const TabsView = ({
{getUserProfile(a.user_created.id)?.name ?? {getUserProfile(a.user_created.id)?.name ??
a.user_created.first_name}{' '} a.user_created.first_name}{' '}
</div> </div>
<div className='tw-text-xs opacity-50 tw-text-zinc-500'> <div className='tw:text-xs opacity-50 tw:text-zinc-500'>
{timeAgo(a.date_created)} {timeAgo(a.date_created)}
</div> </div>
</div> </div>
@ -180,7 +178,7 @@ export const TabsView = ({
) : ( ) : (
<div> <div>
<div className='font-bold'>{a.user_created.first_name} </div> <div className='font-bold'>{a.user_created.first_name} </div>
<div className='tw-text-xs opacity-50 tw-text-zinc-500'> <div className='tw:text-xs opacity-50 tw:text-zinc-500'>
{timeAgo(a.date_created)} {timeAgo(a.date_created)}
</div> </div>
</div> </div>
@ -199,21 +197,21 @@ export const TabsView = ({
type='radio' type='radio'
name='my_tabs_2' name='my_tabs_2'
role='tab' role='tab'
className={`tw-tab tw-font-bold !tw-ps-2 !tw-pe-2 ${!(item.layer.itemType.icon_as_labels && activeTab !== 3) && 'tw-min-w-[10.4em]'} [--tab-border-color:var(--fallback-bc,oklch(var(--bc)/0.2))]`} className={`tw:tab tw:font-bold tw:ps-2! tw:pe-2! ${!(item.layer.itemType.icon_as_labels && activeTab !== 3) && 'tw:min-w-[10.4em]'} `}
aria-label={`${item.layer.itemType.icon_as_labels && activeTab !== 3 ? '♻️' : '♻️\u00A0Offers & Needs'}`} aria-label={`${item.layer.itemType.icon_as_labels && activeTab !== 3 ? '♻️' : '♻️\u00A0Offers & Needs'}`}
checked={activeTab === 3 && true} checked={activeTab === 3 && true}
onChange={() => updateActiveTab(3)} onChange={() => updateActiveTab(3)}
/> />
<div <div
role='tabpanel' role='tabpanel'
className='tw-tab-content tw-bg-base-100 tw-rounded-box tw-h-[calc(100dvh-268px)] tw-overflow-y-auto fade tw-pt-4 tw-pb-1' className='tw:tab-content tw:bg-base-100 tw:rounded-box tw:h-[calc(100dvh-268px)] tw:overflow-y-auto fade tw:pt-4 tw:pb-1'
> >
<div className='tw-h-full'> <div className='tw:h-full'>
<div className='tw-grid tw-grid-cols-1'> <div className='tw:grid tw:grid-cols-1'>
{offers.length > 0 ? ( {offers.length > 0 ? (
<div className='tw-col-span-1'> <div className='tw:col-span-1'>
<h3 className='-tw-mb-2'>Offers</h3> <h3 className='tw:-mb-2'>Offers</h3>
<div className='tw-flex tw-flex-wrap tw-mb-4'> <div className='tw:flex tw:flex-wrap tw:mb-4'>
{offers.map((o) => ( {offers.map((o) => (
<TagView <TagView
key={o.id} key={o.id}
@ -229,9 +227,9 @@ export const TabsView = ({
'' ''
)} )}
{needs.length > 0 ? ( {needs.length > 0 ? (
<div className='tw-col-span-1'> <div className='tw:col-span-1'>
<h3 className='-tw-mb-2 tw-col-span-1'>Needs</h3> <h3 className='tw:-mb-2 tw:col-span-1'>Needs</h3>
<div className='tw-flex tw-flex-wrap tw-mb-4'> <div className='tw:flex tw:flex-wrap tw:mb-4'>
{needs.map((n) => ( {needs.map((n) => (
<TagView key={n.id} tag={n} onClick={() => addFilterTag(n)} /> <TagView key={n.id} tag={n} onClick={() => addFilterTag(n)} />
))} ))}
@ -252,22 +250,22 @@ export const TabsView = ({
type='radio' type='radio'
name='my_tabs_2' name='my_tabs_2'
role='tab' role='tab'
className='tw-tab tw-font-bold !tw-ps-2 !tw-pe-2 [--tab-border-color:var(--fallback-bc,oklch(var(--bc)/0.2))]' className='tw:tab tw:font-bold tw:ps-2! tw:pe-2! '
aria-label={`${item.layer.itemType.icon_as_labels && activeTab !== 7 ? '🔗' : '🔗\u00A0Links'}`} aria-label={`${item.layer.itemType.icon_as_labels && activeTab !== 7 ? '🔗' : '🔗\u00A0Links'}`}
checked={activeTab === 7 && true} checked={activeTab === 7 && true}
onChange={() => updateActiveTab(7)} onChange={() => updateActiveTab(7)}
/> />
<div <div
role='tabpanel' role='tabpanel'
className='tw-tab-content tw-bg-base-100 tw-rounded-box tw-h-[calc(100dvh-280px)] tw-overflow-y-auto tw-pt-4 tw-pb-1 -tw-mr-4 -tw-mb-4 tw-overflow-x-hidden' className='tw:tab-content tw:bg-base-100 tw:rounded-box tw:!h-[calc(100dvh-280px)] tw:overflow-y-auto tw:pt-4 tw:pb-1 tw:-mr-4 tw:-mb-4 tw:overflow-x-hidden'
> >
<div className='tw-h-full'> <div className='tw:h-full'>
<div className='tw-grid tw-grid-cols-1 sm:tw-grid-cols-2 md:tw-grid-cols-1 lg:tw-grid-cols-1 xl:tw-grid-cols-1 2xl:tw-grid-cols-2 tw-pb-4'> <div className='tw:grid tw:grid-cols-1 tw:sm:grid-cols-2 tw:md:grid-cols-1 tw:lg:grid-cols-1 tw:xl:grid-cols-1 tw:2xl:grid-cols-2 tw:pb-4'>
{relations && {relations &&
relations.map((i) => ( relations.map((i) => (
<div <div
key={i.id} key={i.id}
className='tw-cursor-pointer tw-card tw-bg-base-200 tw-border-[1px] tw-border-base-300 tw-card-body tw-shadow-xl tw-text-base-content tw-p-6 tw-mr-4 tw-mb-4' className='tw:cursor-pointer tw:card tw:bg-base-200 tw:border-[1px] tw:border-base-300 tw:card-body tw:shadow-xl tw:text-base-content tw:p-6 tw:mr-4 tw:mb-4'
onClick={() => navigate('/item/' + i.id)} onClick={() => navigate('/item/' + i.id)}
> >
<LinkedItemsHeaderView <LinkedItemsHeaderView
@ -276,7 +274,7 @@ export const TabsView = ({
unlinkCallback={unlinkItem} unlinkCallback={unlinkItem}
loading={loading} loading={loading}
/> />
<div className='tw-overflow-y-auto tw-overflow-x-hidden tw-max-h-64 fade'> <div className='tw:overflow-y-auto tw:overflow-x-hidden tw:max-h-64 fade'>
<TextView truncate text={i.text} itemId={item.id} /> <TextView truncate text={i.text} itemId={item.id} />
</div> </div>
</div> </div>

View File

@ -53,11 +53,11 @@ export function UserSettings() {
return ( return (
<MapOverlayPage <MapOverlayPage
backdrop backdrop
className='tw-mx-4 tw-mt-4 tw-max-h-[calc(100dvh-96px)] tw-h-fit md:tw-w-[calc(50%-32px)] tw-w-[calc(100%-32px)] tw-max-w-xl !tw-left-auto tw-top-0 tw-bottom-0' className='tw:mx-4 tw:mt-4 tw:max-h-[calc(100dvh-96px)] tw:h-fit tw:md:w-[calc(50%-32px)] tw:w-[calc(100%-32px)] tw:max-w-xl tw:left-auto! tw:top-0 tw:bottom-0'
> >
<div className={'tw-text-xl tw-font-semibold'}>Settings</div> <div className={'tw:text-xl tw:font-semibold'}>Settings</div>
<div className='tw-divider tw-mt-2'></div> <div className='tw:divider tw:mt-2'></div>
<div className='tw-grid tw-grid-cols-1 tw-gap-6'> <div className='tw:grid tw:grid-cols-1 tw:gap-6'>
<TextInput <TextInput
type='email' type='email'
placeholder='new E-Mail' placeholder='new E-Mail'
@ -76,12 +76,12 @@ export function UserSettings() {
{/* <ToogleInput updateType="syncData" labelTitle="Sync Data" defaultValue={true} updateFormValue={updateFormValue}/> */} {/* <ToogleInput updateType="syncData" labelTitle="Sync Data" defaultValue={true} updateFormValue={updateFormValue}/> */}
</div> </div>
<div className='tw-mt-8'> <div className='tw:mt-8'>
<button <button
className={ className={
loading loading
? ' tw-loading tw-btn-disabled tw-btn tw-btn-primary tw-float-right' ? ' tw:loading tw:btn-disabled tw:btn tw:btn-primary tw:float-right'
: 'tw-btn tw-btn-primary tw-float-right' : 'tw:btn tw:btn-primary tw:float-right'
} }
onClick={() => onUpdateUser()} onClick={() => onUpdateUser()}
> >

View File

@ -88,16 +88,16 @@ export const AttestationForm = ({ api }: { api?: ItemsApi<unknown> }) => {
const [selectedColor, setSelectedColor] = useState('#fff0d6') const [selectedColor, setSelectedColor] = useState('#fff0d6')
return ( return (
<MapOverlayPage backdrop className='tw-h-fit tw-min-h-56 tw-w-96'> <MapOverlayPage backdrop className='tw:h-fit tw:min-h-56 tw:w-96'>
<div className='tw-text-center tw-text-xl tw-font-bold'>Gratitude</div> <div className='tw:text-center tw:text-xl tw:font-bold'>Gratitude</div>
<div className='tw-text-center tw-text-base tw-text-gray-400'>to</div> <div className='tw:text-center tw:text-base tw:text-gray-400'>to</div>
<div className='tw-flex tw-flex-row tw-justify-center tw-items-center tw-flex-wrap'> <div className='tw:flex tw:flex-row tw:justify-center tw:items-center tw:flex-wrap'>
{users?.map( {users?.map(
(u, k) => ( (u, k) => (
<div key={k} className='tw-flex tw-items-center tw-space-x-3 tw-mx-2 tw-my-1'> <div key={k} className='tw:flex tw:items-center tw:space-x-3 tw:mx-2 tw:my-1'>
{u.image ? ( {u.image ? (
<div className='tw-avatar'> <div className='tw:avatar'>
<div className='tw-mask tw-mask-circle tw-w-8 tw-h-8'> <div className='tw:mask tw:mask-circle tw:w-8 tw:h-8'>
<img <img
src={appState.assetsApi.url + u.image + '?width=40&heigth=40'} src={appState.assetsApi.url + u.image + '?width=40&heigth=40'}
alt='Avatar' alt='Avatar'
@ -105,10 +105,10 @@ export const AttestationForm = ({ api }: { api?: ItemsApi<unknown> }) => {
</div> </div>
</div> </div>
) : ( ) : (
<div className='tw-mask tw-mask-circle tw-text-xl md:tw-text-2xl tw-bg-slate-200 tw-rounded-full tw-w-8 tw-h-8'></div> <div className='tw:mask tw:mask-circle tw:text-xl tw:md:text-2xl tw:bg-slate-200 tw:rounded-full tw:w-8 tw:h-8'></div>
)} )}
<div> <div>
<div className='tw-font-bold'>{u.name}</div> <div className='tw:font-bold'>{u.name}</div>
</div> </div>
</div> </div>
), ),
@ -116,9 +116,9 @@ export const AttestationForm = ({ api }: { api?: ItemsApi<unknown> }) => {
)} )}
</div> </div>
<div className='tw-w-full'> <div className='tw:w-full'>
<div className='tw-flex tw-justify-center tw-items-center'> <div className='tw:flex tw:justify-center tw:items-center'>
<div className=' tw-flex tw-justify-center tw-items-center tw-w-28 tw-h-28 tw-m-4'> <div className=' tw:flex tw:justify-center tw:items-center tw:w-28 tw:h-28 tw:m-4'>
<EmojiPicker <EmojiPicker
selectedEmoji={selectedEmoji} selectedEmoji={selectedEmoji}
selectedColor={selectedColor} selectedColor={selectedColor}
@ -129,19 +129,19 @@ export const AttestationForm = ({ api }: { api?: ItemsApi<unknown> }) => {
/> />
</div> </div>
</div> </div>
<div className='tw-flex tw-justify-center tw-items-center'> <div className='tw:flex tw:justify-center tw:items-center'>
<input <input
ref={inputRef} ref={inputRef}
value={inputValue} value={inputValue}
onChange={handleChange} onChange={handleChange}
type='text' type='text'
placeholder='... and say some words' placeholder='... and say some words'
className='tw-input tw-min-w-0 tw-w-fit tw-resize-none tw-overflow-hidden tw-text-center ' className='input tw:min-w-0 tw:w-fit tw:resize-none tw:overflow-hidden tw:text-center '
/> />
</div> </div>
</div> </div>
<div className='tw-w-full tw-grid tw-mt-4'> <div className='tw:w-full tw:grid tw:mt-4'>
<button onClick={sendAttestation} className='tw-btn tw-place-self-center tw-px-8'> <button onClick={sendAttestation} className='tw:btn tw:place-self-center tw:px-8'>
Next Next
</button> </button>
</div> </div>

View File

@ -17,9 +17,9 @@ export function CardPage({
parents?: { name: string; path: string }[] parents?: { name: string; path: string }[]
}) { }) {
return ( return (
<main className='tw-flex-1 tw-overflow-y-auto tw-overflow-x-hidden tw-pt-2 tw-px-6 tw-min-w-80 tw-flex tw-justify-center'> <main className='tw:flex-1 tw:overflow-y-auto tw:overflow-x-hidden tw:pt-2 tw:px-6 tw:min-w-80 tw:flex tw:justify-center'>
<div className='tw-w-full xl:tw-max-w-6xl '> <div className='tw:w-full tw:xl:max-w-6xl '>
<div className='tw-text-sm tw-breadcrumbs'> <div className='tw:text-sm breadcrumbs'>
<ul> <ul>
<li> <li>
<Link to={'/'}>Home</Link> <Link to={'/'}>Home</Link>
@ -32,7 +32,7 @@ export function CardPage({
<li>{title}</li> <li>{title}</li>
</ul> </ul>
</div> </div>
<TitleCard hideTitle={hideTitle} title={title} topMargin='tw-my-2' className=' tw-mb-4'> <TitleCard hideTitle={hideTitle} title={title} topMargin='tw:my-2' className=' tw:mb-4'>
{children} {children}
</TitleCard> </TitleCard>
</div> </div>

View File

@ -8,24 +8,24 @@ export const DateUserInfo = ({ item }: { item: Item }) => {
const [infoExpanded, setInfoExpanded] = useState<boolean>(false) const [infoExpanded, setInfoExpanded] = useState<boolean>(false)
return ( return (
<div <div
className='tw-flex -tw-mb-1 tw-flex-row tw-mr-2 -tw-mt-2' className='tw:flex tw:-mb-1 tw:flex-row tw:mr-2 tw:-mt-2'
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
{infoExpanded ? ( {infoExpanded ? (
<p <p
className={'tw-italic tw-min-h-[21px] !tw-my-0 tw-text-gray-500'} className={'tw:italic tw:min-h-[21px] tw:my-0! tw:text-gray-500'}
onClick={() => setInfoExpanded(false)} onClick={() => setInfoExpanded(false)}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
>{`${item.date_updated && item.date_updated !== item.date_created ? 'updated' : 'posted'} ${item.user_created?.first_name ? `by ${item.user_created.first_name}` : ''} ${item.date_updated ? timeAgo(item.date_updated) : timeAgo(item.date_created!)}`}</p> >{`${item.date_updated && item.date_updated !== item.date_created ? 'updated' : 'posted'} ${item.user_created?.first_name ? `by ${item.user_created.first_name}` : ''} ${item.date_updated ? timeAgo(item.date_updated) : timeAgo(item.date_created!)}`}</p>
) : ( ) : (
<p <p
className='!tw-my-0 tw-min-h-[21px] tw-font-bold tw-cursor-pointer tw-text-gray-500' className='tw:my-0! tw:min-h-[21px] tw:font-bold tw:cursor-pointer tw:text-gray-500'
onClick={() => setInfoExpanded(true)} onClick={() => setInfoExpanded(true)}
> >
</p> </p>
)} )}
<div className='tw-grow '></div> <div className='tw:grow '></div>
</div> </div>
) )
} }

View File

@ -32,11 +32,11 @@ const DialogModal = ({
useEffect(() => { useEffect(() => {
if (isOpened) { if (isOpened) {
ref.current?.showModal() ref.current?.showModal()
ref.current?.classList.remove('tw-hidden') ref.current?.classList.remove('tw:hidden')
document.body.classList.add('modal-open') // prevent bg scroll document.body.classList.add('modal-open') // prevent bg scroll
} else { } else {
ref.current?.close() ref.current?.close()
ref.current?.classList.add('tw-hidden') ref.current?.classList.add('tw:hidden')
document.body.classList.remove('modal-open') document.body.classList.remove('modal-open')
} }
}, [isOpened]) }, [isOpened])
@ -44,20 +44,20 @@ const DialogModal = ({
if (isOpened) { if (isOpened) {
return ( return (
<dialog <dialog
className={`${className ?? ''} tw-card tw-shadow-xl tw-absolute tw-right-0 tw-top-0 tw-bottom-0 tw-left-0 tw-m-auto tw-transition-opacity tw-duration-300 tw-p-4 tw-max-w-xl tw-bg-base-100`} className={`${className ?? ''} card tw:shadow-xl tw:absolute tw:right-0 tw:top-0 tw:bottom-0 tw:left-0 tw:m-auto tw:transition-opacity tw:duration-300 tw:p-4 tw:max-w-xl tw:bg-base-100`}
ref={ref} ref={ref}
onCancel={onClose} onCancel={onClose}
onClick={(e) => onClick={(e) =>
ref.current && !isClickInsideRectangle(e, ref.current) && closeOnClickOutside && onClose() ref.current && !isClickInsideRectangle(e, ref.current) && closeOnClickOutside && onClose()
} }
> >
<div className='tw-card-body tw-p-2'> <div className='card-body tw:p-2'>
<h2 className='tw-text-2xl tw-font-semibold tw-mb-2 tw-text-center'>{title}</h2> <h2 className='tw:text-2xl tw:font-semibold tw:mb-2 tw:text-center'>{title}</h2>
{children} {children}
{showCloseButton && ( {showCloseButton && (
<button <button
className='tw-btn tw-btn-sm tw-btn-circle tw-btn-ghost tw-absolute tw-right-2 tw-top-2' className='btn btn-sm btn-circle btn-ghost tw:absolute tw:right-2 tw:top-2'
onClick={onClose} onClick={onClose}
> >

View File

@ -97,45 +97,51 @@ export const EmojiPicker = ({
<> <>
<div <div
onClick={toggleDropdown} onClick={toggleDropdown}
className={`tw-cursor-pointer ${selectedEmoji === 'select badge' ? 'tw-text-sm !tw-p-9 tw-text-center ' : 'tw-text-6xl'} tw-mask tw-mask-${selectedShape} tw-p-6 tw-bg-[${selectedColor}]`} className={`tw:cursor-pointer ${selectedEmoji === 'select badge' ? 'tw:text-sm tw:p-9! tw:text-center ' : 'tw:text-6xl'} tw:mask tw:mask-${selectedShape} tw:p-6`}
style={{ backgroundColor: selectedColor }}
> >
{selectedEmoji} {selectedEmoji}
</div> </div>
{isOpen && ( {isOpen && (
<div className='tw-absolute tw-z-3000 tw-top-0 tw-left-1/2 tw-transform tw--translate-x-1/2 tw-mt-12 tw-bg-base-100 tw-rounded-2xl tw-shadow-lg tw-p-2 tw-w-full'> <div className='tw:absolute tw:z-3000 tw:top-0 tw:left-1/2 tw:transform tw:-translate-x-1/2 tw:mt-12 tw:bg-base-100 tw:rounded-2xl tw:shadow-lg tw:p-2 tw:w-full'>
<div className='tw-grid tw-grid-cols-6 tw-gap-2 tw-pb-2'> <div className='tw:grid tw:grid-cols-6 tw:gap-2 tw:pb-2'>
{emojis.map((emoji) => ( {emojis.map((emoji) => (
<button <button
key={emoji} key={emoji}
onClick={() => selectEmoji(emoji)} onClick={() => selectEmoji(emoji)}
className={`tw-cursor-pointer tw-text-2xl tw-p-2 hover:tw-bg-base-200 tw-rounded-md ${emoji === selectedEmoji ? 'tw-bg-base-300' : ''}`} className={`tw:cursor-pointer tw:text-2xl tw:p-2 tw:hover:bg-base-200 tw:rounded-md ${emoji === selectedEmoji ? 'tw:bg-base-300' : ''}`}
> >
{emoji} {emoji}
</button> </button>
))} ))}
</div> </div>
<hr /> <hr />
<div className='tw-grid tw-grid-cols-3 tw-gap-2 tw-py-2'> <div className='tw:grid tw:grid-cols-3 tw:gap-2 tw:py-2'>
{shapes.map((shape) => ( {shapes.map((shape) => (
<div <div
key={shape} key={shape}
className={`tw-cursor-pointer hover:tw-bg-base-200 tw-rounded-md tw-p-2 ${shape === selectedShape ? 'tw-bg-base-300' : ''}`} className={`tw:cursor-pointer tw:hover:bg-base-200 tw:rounded-md tw:p-2 ${shape === selectedShape ? 'tw:bg-base-300' : ''}`}
onClick={() => selectShape(shape)} onClick={() => selectShape(shape)}
> >
<div className={`tw-h-12 tw-mask tw-mask-${shape} tw-bg-neutral-content`}></div> <div
className={`tw:h-12 tw:w-full tw:mask ${shape === 'squircle' ? 'tw:mask-squircle' : shape === 'circle' ? 'tw:mask-circle' : 'tw:mask-hexagon-2'} tw:bg-neutral-content`}
></div>
</div> </div>
))} ))}
</div> </div>
<hr /> <hr />
<div className='tw-grid tw-grid-cols-6 tw-gap-2 tw-py-2 tw-px-6'> <div className='tw:grid tw:grid-cols-6 tw:gap-2 tw:py-2 tw:px-6'>
{colors.map((color) => ( {colors.map((color) => (
<div <div
key={color} key={color}
className={`tw-cursor-pointer hover:tw-bg-base-200 tw-rounded-md tw-p-2 tw-flex tw-justify-center tw-items-center ${color === selectedColor ? 'tw-bg-base-300' : ''}`} className={`tw:cursor-pointer tw:hover:bg-base-200 tw:rounded-md tw:p-2 tw:flex tw:justify-center tw:items-center ${color === selectedColor ? 'tw:bg-base-300' : ''}`}
onClick={() => selectColor(color)} onClick={() => selectColor(color)}
> >
<div className={`tw-h-8 tw-w-8 tw-rounded-full tw-bg-[${color}]`}></div> <div
className={`tw:h-8 tw:w-8 tw:rounded-full`}
style={{ backgroundColor: color }}
></div>
</div> </div>
))} ))}
</div> </div>

View File

@ -24,7 +24,7 @@ export const ItemCard = ({
return ( return (
<div <div
className='tw-cursor-pointer tw-card tw-border-[1px] tw-border-base-300 tw-card-body tw-shadow-xl tw-bg-base-100 tw-text-base-content tw-p-4 tw-mb-4 tw-h-fit' className='tw:cursor-pointer tw:card tw:border-[1px] tw:border-base-300 tw:card-body tw:shadow-xl tw:bg-base-100 tw:text-base-content tw:p-4 tw:mb-4 tw:h-fit'
onClick={() => { onClick={() => {
// We could have an onClick callback instead // We could have an onClick callback instead
const params = new URLSearchParams(window.location.search) const params = new URLSearchParams(window.location.search)
@ -40,7 +40,7 @@ export const ItemCard = ({
editCallback={() => navigate('/edit-item/' + i.id)} editCallback={() => navigate('/edit-item/' + i.id)}
deleteCallback={() => deleteCallback(i)} deleteCallback={() => deleteCallback(i)}
></HeaderView> ></HeaderView>
<div className='tw-overflow-y-auto tw-overflow-x-hidden tw-max-h-64 fade'> <div className='tw:overflow-y-auto tw:overflow-x-hidden tw:max-h-64 fade'>
{i.layer?.itemType.show_start_end && <StartEndView item={i}></StartEndView>} {i.layer?.itemType.show_start_end && <StartEndView item={i}></StartEndView>}
{i.layer?.itemType.show_text && <TextView truncate text={i.text} itemId={i.id} />} {i.layer?.itemType.show_text && <TextView truncate text={i.text} itemId={i.id} />}
</div> </div>

View File

@ -38,18 +38,18 @@ export function MapOverlayPage({
}, [overlayRef, backdropRef]) }, [overlayRef, backdropRef])
return ( return (
<div className={`tw-absolute tw-h-full tw-w-full tw-m-auto ${backdrop ? 'tw-z-[2000]' : ''}`}> <div className={`tw:absolute tw:h-full tw:w-full tw:m-auto ${backdrop ? 'tw:z-2000' : ''}`}>
<div <div
ref={backdropRef} ref={backdropRef}
className={`${backdrop ? 'tw-backdrop-brightness-75' : ''} tw-h-full tw-w-full tw-grid tw-place-items-center tw-m-auto`} className={`${backdrop ? 'tw:backdrop-brightness-75' : ''} tw:h-full tw:w-full tw:grid tw:place-items-center tw:m-auto`}
> >
<div <div
ref={overlayRef} ref={overlayRef}
className={`${card ? 'tw-card tw-card-body' : ''} tw-shadow-xl tw-bg-base-100 tw-p-6 ${className ?? ''} ${backdrop ? '' : 'tw-z-[2000]'} tw-absolute tw-top-0 tw-bottom-0 tw-right-0 tw-left-0 tw-m-auto`} className={`${card ? 'tw:card tw:card-body' : ''} tw:shadow-xl tw:bg-base-100 tw:p-6 ${className ?? ''} ${backdrop ? '' : 'tw:z-2000'} tw:absolute tw:top-0 tw:bottom-0 tw:right-0 tw:left-0 tw:m-auto`}
> >
{children} {children}
<button <button
className='tw-btn tw-btn-sm tw-btn-circle tw-btn-ghost tw-absolute tw-right-2 tw-top-2' className='tw:btn tw:btn-sm tw:btn-circle tw:btn-ghost tw:absolute tw:right-2 tw:top-2'
onClick={() => closeScreen()} onClick={() => closeScreen()}
> >

View File

@ -61,11 +61,11 @@ export const MarketView = () => {
}, [items]) }, [items])
return ( return (
<MapOverlayPage className='tw-rounded-none tw-overflow-y-auto tw-bg-base-200 !tw-p-4'> <MapOverlayPage className='tw:rounded-none tw:overflow-y-auto tw:bg-base-200 tw:p-4!'>
<div className='tw-grid tw-grid-cols-1 md:tw-grid-cols-2'> <div className='tw:grid tw:grid-cols-1 tw:md:grid-cols-2'>
<div> <div>
<p className='tw-text-lg tw-font-bold'>Offers</p> <p className='tw:text-lg tw:font-bold'>Offers</p>
<div className='tw-flex tw-flex-wrap'> <div className='tw:flex tw:flex-wrap'>
{groupAndCount(offers).map((o) => ( {groupAndCount(offers).map((o) => (
<TagView <TagView
onClick={() => navigate(`/?tags=${o.object.name}`)} onClick={() => navigate(`/?tags=${o.object.name}`)}
@ -77,8 +77,8 @@ export const MarketView = () => {
</div> </div>
</div> </div>
<div> <div>
<p className='tw-text-lg tw-font-bold'>Needs</p> <p className='tw:text-lg tw:font-bold'>Needs</p>
<div className='tw-flex tw-flex-wrap'> <div className='tw:flex tw:flex-wrap'>
{groupAndCount(needs).map((o) => ( {groupAndCount(needs).map((o) => (
<TagView <TagView
onClick={() => navigate(`/?tags=${o.object.name}`)} onClick={() => navigate(`/?tags=${o.object.name}`)}

View File

@ -128,16 +128,16 @@ export const OverlayItemsIndexPage = ({
return ( return (
<> <>
<MapOverlayPage className='tw-rounded-none tw-overflow-y-auto tw-bg-base-200 !tw-p-4'> <MapOverlayPage className='tw:rounded-none tw:overflow-y-auto tw:bg-base-200 tw:p-4!'>
<div className='tw-flex tw-flex-col tw-h-full'> <div className='tw:flex tw:flex-col tw:h-full'>
<div className='tw-flex-none'> <div className='tw:flex-none'>
<Control position='topLeft' zIndex='1000' absolute={false}> <Control position='topLeft' zIndex='1000' absolute={false}>
<SearchControl /> <SearchControl />
<TagsControl /> <TagsControl />
</Control> </Control>
</div> </div>
<div className='tw-overflow-scroll fade tw-flex-1'> <div className='tw:overflow-scroll fade tw:flex-1'>
<div className='tw-columns-1 md:tw-columns-2 lg:tw-columns-3 2xl:tw-columns-4 tw-gap-6 tw-pt-4'> <div className='tw:columns-1 tw:md:columns-2 tw:lg:columns-3 tw:2xl:columns-4 tw:gap-6 tw:pt-4'>
{items {items
.filter((i) => i.layer?.name === layerName) .filter((i) => i.layer?.name === layerName)
.filter((item) => .filter((item) =>
@ -165,7 +165,7 @@ export const OverlayItemsIndexPage = ({
return dateB - dateA // Subtracts milliseconds which are numbers return dateB - dateA // Subtracts milliseconds which are numbers
}) })
.map((i, k) => ( .map((i, k) => (
<div key={k} className='tw-break-inside-avoid tw-mb-6'> <div key={k} className='tw:break-inside-avoid tw:mb-6'>
<ItemCard <ItemCard
i={i} i={i}
loading={loading} loading={loading}
@ -176,12 +176,12 @@ export const OverlayItemsIndexPage = ({
))} ))}
{addItemPopupType === 'place' && ( {addItemPopupType === 'place' && (
<form ref={tabRef} autoComplete='off' onSubmit={(e) => submitNewItem(e)}> <form ref={tabRef} autoComplete='off' onSubmit={(e) => submitNewItem(e)}>
<div className='tw-cursor-pointer tw-break-inside-avoid tw-card tw-border-[1px] tw-border-base-300 tw-card-body tw-shadow-xl tw-bg-base-100 tw-text-base-content tw-p-6 tw-mb-10'> <div className='tw:cursor-pointer tw:break-inside-avoid card tw:border-[1px] tw:border-base-300 card-body tw:shadow-xl tw:bg-base-100 tw:text-base-content tw:p-6 tw:mb-10'>
<label <label
className='tw-btn tw-btn-sm tw-rounded-2xl tw-btn-circle tw-btn-ghost hover:tw-bg-transparent tw-absolute tw-right-0 tw-top-0 tw-text-gray-600' className='btn btn-sm tw:rounded-2xl btn-circle btn-ghost tw:hover:bg-transparent tw:absolute tw:right-0 tw:top-0 tw:text-gray-600'
onClick={() => setAddItemPopupType('')} onClick={() => setAddItemPopupType('')}
> >
<p className='tw-text-center'></p> <p className='tw:text-center'></p>
</label> </label>
<TextInput <TextInput
type='text' type='text'
@ -195,18 +195,18 @@ export const OverlayItemsIndexPage = ({
placeholder='Text' placeholder='Text'
dataField='text' dataField='text'
defaultValue={''} defaultValue={''}
inputStyle='tw-h-40 tw-mt-5' inputStyle='tw:h-40 tw:mt-5'
/> />
<div className='tw-flex tw-justify-center'> <div className='tw:flex tw:justify-center'>
<button <button
className={ className={
loading loading
? 'tw-btn tw-btn-disabled tw-mt-5 tw-place-self-center' ? 'btn btn-disabled tw:mt-5 tw:place-self-center'
: 'tw-btn tw-mt-5 tw-place-self-center' : 'btn tw:mt-5 tw:place-self-center'
} }
type='submit' type='submit'
> >
{loading ? <span className='tw-loading tw-loading-spinner'></span> : 'Save'} {loading ? <span className='loading loading-spinner'></span> : 'Save'}
</button> </button>
</div> </div>
</div> </div>

View File

@ -18,12 +18,12 @@ export const SelectUser = () => {
const [selectedUsers, setSelectedUsers] = useState<string[]>([]) const [selectedUsers, setSelectedUsers] = useState<string[]>([])
return ( return (
<MapOverlayPage backdrop className='tw-h-3/4 tw-w-80'> <MapOverlayPage backdrop className='tw:h-3/4 tw:w-80'>
<div className='tw-text-center tw-text-xl tw-font-bold tw-mb-4'>Gratitude to ...</div> <div className='tw:text-center tw:text-xl tw:font-bold tw:mb-4'>Gratitude to ...</div>
{/* Team Member list in table format loaded constant */} {/* Team Member list in table format loaded constant */}
<div className='tw-overflow-x-auto tw-w-full fade'> <div className='tw:overflow-x-auto tw:w-full fade'>
<table className='tw-table tw-w-full'> <table className='tw:table tw:w-full'>
<tbody> <tbody>
{users.map((u, k) => { {users.map((u, k) => {
return ( return (
@ -32,14 +32,14 @@ export const SelectUser = () => {
<input <input
type='checkbox' type='checkbox'
onChange={() => setSelectedUsers((prev) => [...prev, u.id])} onChange={() => setSelectedUsers((prev) => [...prev, u.id])}
className='tw-checkbox tw-checkbox-sm' className='checkbox checkbox-sm'
/> />
</td> </td>
<td> <td>
<div className='tw-flex tw-items-center tw-space-x-3'> <div className='tw:flex tw:items-center tw:space-x-3'>
{u.image ? ( {u.image ? (
<div className='tw-avatar'> <div className='tw:avatar'>
<div className='tw-mask tw-mask-circle tw-w-8 tw-h-8'> <div className='tw:mask tw:mask-circle tw:w-8 tw:h-8'>
<img <img
src={appState.assetsApi.url + u.image + '?width=40&heigth=40'} src={appState.assetsApi.url + u.image + '?width=40&heigth=40'}
alt='Avatar' alt='Avatar'
@ -47,10 +47,10 @@ export const SelectUser = () => {
</div> </div>
</div> </div>
) : ( ) : (
<div className='tw-mask tw-mask-circle tw-text-xl md:tw-text-2xl tw-bg-slate-200 tw-rounded-full tw-w-8 tw-h-8'></div> <div className='tw:mask tw:mask-circle tw:text-xl tw:md:text-2xl tw:bg-slate-200 tw:rounded-full tw:w-8 tw:h-8'></div>
)} )}
<div> <div>
<div className='tw-font-bold'>{u.name}</div> <div className='tw:font-bold'>{u.name}</div>
</div> </div>
</div> </div>
</td> </td>
@ -60,12 +60,12 @@ export const SelectUser = () => {
</tbody> </tbody>
</table> </table>
</div> </div>
<div className='tw-w-full tw-grid tw-mt-4'> <div className='tw:w-full tw:grid tw:mt-4'>
<Link <Link
className='tw-place-self-center ' className='tw:place-self-center '
to={'/attestation-form' + '?to=' + selectedUsers.map((u) => u, ',')} to={'/attestation-form' + '?to=' + selectedUsers.map((u) => u, ',')}
> >
<button className='tw-btn tw-px-8'>Next</button> <button className='tw:btn tw:px-8'>Next</button>
</Link> </Link>
</div> </div>
</MapOverlayPage> </MapOverlayPage>

View File

@ -19,11 +19,11 @@ export const TagView = ({
<div <div
key={tag.name} key={tag.name}
onClick={onClick} onClick={onClick}
className={`tw-flex tw-items-center tw-flex-row tw-rounded-2xl tw-text-white tw-p-2 tw-px-4 tw-shadow-xl tw-card tw-mt-3 tw-mr-4 tw-cursor-pointer tw-w-fit ${heighlight ? 'tw-border-4 tw-border-base-200 tw-shadow-lg' : ''}`} className={`tw:flex tw:items-center tw:flex-row tw:rounded-2xl tw:text-white tw:p-2 tw:px-4 tw:shadow-xl card tw:mt-3 tw:mr-4 tw:cursor-pointer tw:w-fit ${heighlight ? 'tw:border-4 tw:border-base-200 tw:shadow-lg' : ''}`}
style={{ backgroundColor: tag.color ? tag.color : '#666' }} style={{ backgroundColor: tag.color ? tag.color : '#666' }}
> >
<b>{decodeTag(tag.name)}</b> <b>{decodeTag(tag.name)}</b>
{count && <span className='tw-ml-2'>({count})</span>} {count && <span className='tw:ml-2'>({count})</span>}
</div> </div>
) )
} }

View File

@ -0,0 +1,63 @@
import { useState, useEffect } from 'react'
const themes = [
'default',
'light',
'dark',
'valentine',
'retro',
'aqua',
'cyberpunk',
'caramellatte',
'abyss',
'silk',
]
export const ThemeControl = () => {
const [theme, setTheme] = useState<string>(() => {
const savedTheme = localStorage.getItem('theme')
return savedTheme ? (JSON.parse(savedTheme) as string) : 'default'
})
useEffect(() => {
if (theme !== 'default') {
localStorage.setItem('theme', JSON.stringify(theme))
} else localStorage.removeItem('theme')
document.documentElement.setAttribute('data-theme', theme)
}, [theme])
return (
<div className='tw:dropdown tw:mr-2'>
<div tabIndex={0} role='button' className='tw:btn tw:m-1'>
Theme
<svg
width='12px'
height='12px'
className='tw:inline-block tw:h-2 tw:w-2 tw:fill-current tw:opacity-60'
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 2048 2048'
>
<path d='M1799 349l242 241-1017 1017L7 590l242-241 775 775 775-775z'></path>
</svg>
</div>
<ul
tabIndex={0}
className='tw:dropdown-content tw:bg-base-200 tw:rounded-box tw:z-1 tw:w-36 tw:p-2 tw:shadow-2xl'
>
{themes.map((t) => (
<li key={t}>
<input
className={`tw:btn ${theme === t ? 'tw:bg-base-300' : ''} tw:btn-sm tw:btn-block tw:btn-ghost tw:justify-start`}
type='radio'
name='theme'
value={t}
checked={theme === t}
onChange={() => setTheme(t)}
aria-label={t.toLowerCase()}
/>
</li>
))}
</ul>
</div>
)
}

View File

@ -23,28 +23,28 @@ export function TitleCard({
return ( return (
<div <div
className={ className={
'tw-card tw-w-full tw-p-6 tw-bg-base-100 tw-shadow-xl tw-h-fit tw-mb-4 ' + 'card tw:w-full tw:p-6 tw:bg-base-100 tw:shadow-xl tw:h-fit tw:mb-4 ' +
(className ?? '') + (className ?? '') +
' ' + ' ' +
(topMargin ?? 'tw-mt-6') (topMargin ?? 'tw:mt-6')
} }
> >
{!hideTitle && ( {!hideTitle && (
<> <>
<Subtitle styleClass={TopSideButtons ? 'tw-inline-block' : ''}> <Subtitle styleClass={TopSideButtons ? 'tw:inline-block' : ''}>
{title} {title}
{/* Top side button, show only if present */} {/* Top side button, show only if present */}
{TopSideButtons && ( {TopSideButtons && (
<div className='tw-inline-block tw-float-right'>{TopSideButtons}</div> <div className='tw:inline-block tw:float-right'>{TopSideButtons}</div>
)} )}
</Subtitle> </Subtitle>
<div className='tw-divider tw-mt-2'></div> <div className='divider tw:mt-2'></div>
</> </>
)} )}
{/** Card Body */} {/** Card Body */}
<div className='tw-h-full tw-bg-transparent tw-w-full tw-pb-6 tw-bg-base-100'>{children}</div> <div className='tw:h-full tw:bg-transparent tw:w-full tw:pb-6 tw:bg-base-100'>{children}</div>
</div> </div>
) )
} }

View File

@ -1,5 +1,5 @@
function ErrorText({ styleClass, children }: { styleClass: string; children: React.ReactNode }) { function ErrorText({ styleClass, children }: { styleClass: string; children: React.ReactNode }) {
return <p className={`tw-text-center tw-text-error ${styleClass}`}>{children}</p> return <p className={`tw:text-center tw:text-error ${styleClass}`}>{children}</p>
} }
export default ErrorText export default ErrorText

View File

@ -1,5 +1,5 @@
function Subtitle({ styleClass, children }: { styleClass: string; children: React.ReactNode }) { function Subtitle({ styleClass, children }: { styleClass: string; children: React.ReactNode }) {
return <div className={`tw-text-xl tw-font-semibold ${styleClass}`}>{children}</div> return <div className={`tw:text-xl tw:font-semibold ${styleClass}`}>{children}</div>
} }
export default Subtitle export default Subtitle

View File

@ -2,7 +2,7 @@
xmlns='http://www.w3.org/2000/svg' xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 24 24' viewBox='0 0 24 24'
fill='white' fill='white'
className='tw-h-4 tw-w-4' className='h-4 w-4'
> >
<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 4l-8 5-8-5V6l8 5 8-5v2z' /> <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 4l-8 5-8-5V6l8 5 8-5v2z' />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 310 B

After

Width:  |  Height:  |  Size: 304 B

View File

@ -20,26 +20,25 @@
} }
.leaflet-popup-content-wrapper, .leaflet-popup-tip{ .leaflet-popup-content-wrapper, .leaflet-popup-tip{
background-color: theme('colors.base-100'); background-color: var(--color-base-100);
color: theme('colors.base-content'); color: var(--color-base-content);
border-radius: var(--radius-box);
} }
.leaflet-tooltip { .leaflet-tooltip {
background-color: theme('colors.base-100'); background-color: var(--color-base-100);
color: theme('colors.base-content'); color: var(--color-base-content);
border-width: 0px; border-width: 0px;
} }
.leaflet-tooltip { .leaflet-tooltip {
border-radius: 1em; border-radius: var(--radius-box);
transition: opacity 500ms; transition: opacity 500ms;
transition-delay: 50ms; transition-delay: 50ms;
} }
.leaflet-tooltip-top::before { .leaflet-tooltip::before {
border-top-color: theme('colors.base-100'); border-top-color: var(--color-base-100);
} }
.leaflet-container { .leaflet-container {
@ -48,4 +47,9 @@
background-repeat: no-repeat; background-repeat: no-repeat;
background-attachment: fixed; background-attachment: fixed;
background-position: 50% 80%; background-position: 50% 80%;
}
.leaflet-popup-close-button span {
color: var(--color-base-content);
opacity: 50%;
} }

View File

@ -1,24 +1,80 @@
@tailwind base; @import 'tailwindcss' prefix(tw);
@tailwind components; @plugin "daisyui" {
@tailwind utilities; themes: light --default, dark --prefersdark, valentine, retro, aqua, cyberpunk, caramellatte, abyss, silk;
}
.tw-modal { @theme {
--animate-*: initial;
--animate-pulse-grow: pulseGrow 2s ease-in-out infinite;
--z-index-500: 500;
--z-index-1000: 1000;
--z-index-2000: 2000;
--z-index-3000: 3000;
--min-width-56: 224px;
--min-width-64: 240px;
--min-width-80: 320px;
--container-52: 208px;
--container-64: 240px;
--container-72: 288px;
--min-height-56: 224px;
--min-height-64: 240px;
--min-height-80: 320px;
--font-sans: Helvetica, sans-serif, Roboto;
--text-map: 13px;
--leading-map: 1.4em;
@keyframes pulseGrow {
0%,
100% {
transform: scale(1);
}
80% {
transform: scale(1);
}
90% {
transform: scale(0.95);
}
}
}
/*
The default border color has changed to `currentColor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
looks the same as it did with Tailwind CSS v3.
If we ever want to remove these styles, we need to add an explicit border
color utility to any element that depends on these defaults.
*/
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentColor);
}
}
.modal {
z-index: 1200 !important; z-index: 1200 !important;
} }
.tw-menu li a { .menu li a {
border-radius: 10px; border-radius: 10px;
} }
.tw-modal { .modal {
z-index: 1200 !important; z-index: 1200 !important;
max-height: 100dvh; max-height: 100dvh;
} }
.tw-modal-box { .modal-box {
max-height: calc(100dvh - 2em); max-height: calc(100dvh - 2em);
}
.tw-tab-content .container {
height: 100%;
} }

View File

@ -1,20 +1,20 @@
:root { :root {
--toastify-color-info: theme('colors.info'); --toastify-color-info: var(--color-info);
--toastify-color-success: theme('colors.success'); --toastify-color-success: var(--color-success);
--toastify-color-warning: theme('colors.warning'); --toastify-color-warning: var(--color-warning);
--toastify-color-error: theme('colors.error'); --toastify-color-error: var(--color-error);
} }
.Toastify__toast { .Toastify__toast {
border-radius: 1rem; border-radius: var(--radius-box);
--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); --shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color); --shadow-colored: 0 20px 25px -5px var(--shadow-color), 0 8px 10px -6px var(--shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); box-shadow: var(--ring-offset-shadow, 0 0 #0000), var(--ring-shadow, 0 0 #0000), var(--shadow);
margin-left: 1rem; margin-left: 1rem;
margin-right: 1rem; margin-right: 1rem;
margin-bottom: 1rem; margin-bottom: 1rem;
background-color: theme('colors.base-100'); background-color: var(--color-base-100);
color: theme('colors.base-content'); color: var(--color-base-content);
} }
.Toastify__toast-container { .Toastify__toast-container {

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="3" stroke="currentColor" class="tw-w-5 tw-h-5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="3" stroke="currentColor" class="w-5 h-5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"></path></svg>

Before

Width:  |  Height:  |  Size: 226 B

After

Width:  |  Height:  |  Size: 220 B

Some files were not shown because too many files have changed in this diff Show More