mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
Merge pull request #29 from utopia-os/flex-profile
fix(other): flex profile
This commit is contained in:
commit
05b095e6fb
2
package-lock.json
generated
2
package-lock.json
generated
@ -7,7 +7,7 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "utopia-ui",
|
"name": "utopia-ui",
|
||||||
"version": "3.0.10",
|
"version": "3.0.10",
|
||||||
"license": "MIT",
|
"license": "GPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@heroicons/react": "^2.0.17",
|
"@heroicons/react": "^2.0.17",
|
||||||
"@tanstack/react-query": "^5.17.8",
|
"@tanstack/react-query": "^5.17.8",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import NavBar from './NavBar'
|
import NavBar from './NavBar'
|
||||||
import { SetAssetsApi } from './SetAssetsApi'
|
import { SetAppState } from './SetAppState'
|
||||||
import { AssetsApi } from '../../types'
|
import { AssetsApi } from '../../types'
|
||||||
import { ContextWrapper } from './ContextWrapper'
|
import { ContextWrapper } from './ContextWrapper'
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ 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'>
|
||||||
<SetAssetsApi assetsApi={assetsApi} />
|
<SetAppState assetsApi={assetsApi} userType={userType} />
|
||||||
<NavBar userType={userType} appName={appName}></NavBar>
|
<NavBar userType={userType} appName={appName}></NavBar>
|
||||||
<div id='app-content' className='tw-flex-grow'>
|
<div id='app-content' className='tw-flex-grow'>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { LeafletRefsProvider } from '../Map/hooks/useLeafletRefs'
|
|||||||
import { PermissionsProvider } from '../Map/hooks/usePermissions'
|
import { PermissionsProvider } from '../Map/hooks/usePermissions'
|
||||||
import { SelectPositionProvider } from '../Map/hooks/useSelectPosition'
|
import { SelectPositionProvider } from '../Map/hooks/useSelectPosition'
|
||||||
import { TagsProvider } from '../Map/hooks/useTags'
|
import { TagsProvider } from '../Map/hooks/useTags'
|
||||||
import { AssetsProvider } from './hooks/useAssets'
|
import { AppStateProvider } from './hooks/useAppState'
|
||||||
import { useContext, createContext } from 'react'
|
import { useContext, createContext } from 'react'
|
||||||
import { BrowserRouter as Router, useLocation } from 'react-router-dom'
|
import { BrowserRouter as Router, useLocation } from 'react-router-dom'
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ export const Wrappers = ({ children }) => {
|
|||||||
<SelectPositionProvider>
|
<SelectPositionProvider>
|
||||||
<LeafletRefsProvider initialLeafletRefs={{}}>
|
<LeafletRefsProvider initialLeafletRefs={{}}>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<AssetsProvider>
|
<AppStateProvider>
|
||||||
<ClusterRefProvider>
|
<ClusterRefProvider>
|
||||||
<QuestsProvider initialOpen={true}>
|
<QuestsProvider initialOpen={true}>
|
||||||
<ToastContainer
|
<ToastContainer
|
||||||
@ -89,7 +89,7 @@ export const Wrappers = ({ children }) => {
|
|||||||
{children}
|
{children}
|
||||||
</QuestsProvider>
|
</QuestsProvider>
|
||||||
</ClusterRefProvider>
|
</ClusterRefProvider>
|
||||||
</AssetsProvider>
|
</AppStateProvider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
</LeafletRefsProvider>
|
</LeafletRefsProvider>
|
||||||
</SelectPositionProvider>
|
</SelectPositionProvider>
|
||||||
|
|||||||
24
src/Components/AppShell/SetAppState.tsx
Normal file
24
src/Components/AppShell/SetAppState.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { useSetAppState } from './hooks/useAppState'
|
||||||
|
import { AssetsApi } from '../../types'
|
||||||
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
|
export const SetAppState = ({
|
||||||
|
assetsApi,
|
||||||
|
userType,
|
||||||
|
}: {
|
||||||
|
assetsApi: AssetsApi
|
||||||
|
userType: string
|
||||||
|
}) => {
|
||||||
|
const setAppState = useSetAppState()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setAppState({ assetsApi })
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [assetsApi])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setAppState({ userType })
|
||||||
|
}, [setAppState, userType])
|
||||||
|
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
@ -1,14 +0,0 @@
|
|||||||
import { useSetAssetApi } from './hooks/useAssets'
|
|
||||||
import { AssetsApi } from '../../types'
|
|
||||||
import { useEffect } from 'react'
|
|
||||||
|
|
||||||
export const SetAssetsApi = ({ assetsApi }: { assetsApi: AssetsApi }) => {
|
|
||||||
const setAssetsApi = useSetAssetApi()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setAssetsApi(assetsApi)
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [assetsApi])
|
|
||||||
|
|
||||||
return <></>
|
|
||||||
}
|
|
||||||
50
src/Components/AppShell/hooks/useAppState.tsx
Normal file
50
src/Components/AppShell/hooks/useAppState.tsx
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { useCallback, useState, createContext, useContext } from 'react'
|
||||||
|
import * as React from 'react'
|
||||||
|
import { AssetsApi } from '../../../types'
|
||||||
|
|
||||||
|
type AppState = {
|
||||||
|
assetsApi: AssetsApi
|
||||||
|
userType: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type UseAppManagerResult = ReturnType<typeof useAppManager>
|
||||||
|
|
||||||
|
const initialAppState: AppState = {
|
||||||
|
assetsApi: {} as AssetsApi,
|
||||||
|
userType: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
const AppContext = createContext<UseAppManagerResult>({
|
||||||
|
state: initialAppState,
|
||||||
|
setAppState: () => {},
|
||||||
|
})
|
||||||
|
|
||||||
|
function useAppManager(): {
|
||||||
|
state: AppState
|
||||||
|
setAppState: (newState: Partial<AppState>) => void
|
||||||
|
} {
|
||||||
|
const [state, setState] = useState<AppState>(initialAppState)
|
||||||
|
|
||||||
|
const setAppState = useCallback((newState: Partial<AppState>) => {
|
||||||
|
setState((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
...newState,
|
||||||
|
}))
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return { state, setAppState }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AppStateProvider: React.FunctionComponent<{
|
||||||
|
children?: React.ReactNode
|
||||||
|
}> = ({ children }) => <AppContext.Provider value={useAppManager()}>{children}</AppContext.Provider>
|
||||||
|
|
||||||
|
export const useAppState = (): AppState => {
|
||||||
|
const { state } = useContext(AppContext)
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useSetAppState = (): UseAppManagerResult['setAppState'] => {
|
||||||
|
const { setAppState } = useContext(AppContext)
|
||||||
|
return setAppState
|
||||||
|
}
|
||||||
@ -1,40 +0,0 @@
|
|||||||
import { useCallback, useState, createContext, useContext } from 'react'
|
|
||||||
|
|
||||||
import * as React from 'react'
|
|
||||||
import { AssetsApi } from '../../../types'
|
|
||||||
|
|
||||||
type UseAssetManagerResult = ReturnType<typeof useAssetsManager>
|
|
||||||
|
|
||||||
const AssetContext = createContext<UseAssetManagerResult>({
|
|
||||||
api: {} as AssetsApi,
|
|
||||||
setAssetsApi: () => {},
|
|
||||||
})
|
|
||||||
|
|
||||||
function useAssetsManager(): {
|
|
||||||
api: AssetsApi
|
|
||||||
setAssetsApi: (api: AssetsApi) => void
|
|
||||||
} {
|
|
||||||
const [api, setApi] = useState<AssetsApi>({} as AssetsApi)
|
|
||||||
|
|
||||||
const setAssetsApi = useCallback((api: AssetsApi) => {
|
|
||||||
setApi(api)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return { api, setAssetsApi }
|
|
||||||
}
|
|
||||||
|
|
||||||
export const AssetsProvider: React.FunctionComponent<{
|
|
||||||
children?: React.ReactNode
|
|
||||||
}> = ({ children }) => (
|
|
||||||
<AssetContext.Provider value={useAssetsManager()}>{children}</AssetContext.Provider>
|
|
||||||
)
|
|
||||||
|
|
||||||
export const useAssetApi = (): AssetsApi => {
|
|
||||||
const { api } = useContext(AssetContext)
|
|
||||||
return api
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useSetAssetApi = (): UseAssetManagerResult['setAssetsApi'] => {
|
|
||||||
const { setAssetsApi } = useContext(AssetContext)
|
|
||||||
return setAssetsApi
|
|
||||||
}
|
|
||||||
@ -1,20 +1,15 @@
|
|||||||
import { useState } from 'react'
|
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
|
|
||||||
interface ComboBoxProps {
|
interface ComboBoxProps {
|
||||||
id?: string
|
id?: string
|
||||||
options: { value: string; label: string }[]
|
options: string[]
|
||||||
value: string
|
value: string
|
||||||
onValueChange: (newValue: string) => void
|
onValueChange: (newValue: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const ComboBoxInput = ({ id, options, value, onValueChange }: ComboBoxProps) => {
|
const ComboBoxInput = ({ id, options, value, onValueChange }: ComboBoxProps) => {
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const [selectedValue, setSelectedValue] = useState(value)
|
|
||||||
|
|
||||||
const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
const value = e.target.value
|
const value = e.target.value
|
||||||
setSelectedValue(value)
|
|
||||||
onValueChange(value)
|
onValueChange(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,8 +20,8 @@ const ComboBoxInput = ({ id, options, value, onValueChange }: ComboBoxProps) =>
|
|||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
>
|
>
|
||||||
{options.map((o) => (
|
{options.map((o) => (
|
||||||
<option value={o.value} key={o.value} selected={o.value === value}>
|
<option value={o} key={o} selected={o === value}>
|
||||||
{o.label}
|
{o}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import * as React from 'react'
|
|||||||
import { Item, ItemsApi } from '../../../../types'
|
import { Item, ItemsApi } from '../../../../types'
|
||||||
import { useHasUserPermission } from '../../hooks/usePermissions'
|
import { useHasUserPermission } from '../../hooks/usePermissions'
|
||||||
import { getValue } from '../../../../Utils/GetValue'
|
import { getValue } from '../../../../Utils/GetValue'
|
||||||
import { useAssetApi } from '../../../AppShell/hooks/useAssets'
|
import { useAppState } from '../../../AppShell/hooks/useAppState'
|
||||||
import DialogModal from '../../../Templates/DialogModal'
|
import DialogModal from '../../../Templates/DialogModal'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
@ -41,17 +41,17 @@ export function HeaderView({
|
|||||||
|
|
||||||
const hasUserPermission = useHasUserPermission()
|
const hasUserPermission = useHasUserPermission()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const assetsApi = useAssetApi()
|
const appState = useAppState()
|
||||||
|
|
||||||
const avatar =
|
const avatar =
|
||||||
itemAvatarField && getValue(item, itemAvatarField)
|
itemAvatarField && getValue(item, itemAvatarField)
|
||||||
? assetsApi.url +
|
? appState.assetsApi.url +
|
||||||
getValue(item, itemAvatarField) +
|
getValue(item, itemAvatarField) +
|
||||||
`${big ? '?width=160&heigth=160' : '?width=80&heigth=80'}`
|
`${big ? '?width=160&heigth=160' : '?width=80&heigth=80'}`
|
||||||
: item.layer?.itemAvatarField &&
|
: item.layer?.itemAvatarField &&
|
||||||
item &&
|
item &&
|
||||||
getValue(item, item.layer?.itemAvatarField) &&
|
getValue(item, item.layer?.itemAvatarField) &&
|
||||||
assetsApi.url +
|
appState.assetsApi.url +
|
||||||
getValue(item, item.layer?.itemAvatarField) +
|
getValue(item, item.layer?.itemAvatarField) +
|
||||||
`${big ? '?width=160&heigth=160' : '?width=80&heigth=80'}`
|
`${big ? '?width=160&heigth=160' : '?width=80&heigth=80'}`
|
||||||
const title = itemNameField
|
const title = itemNameField
|
||||||
|
|||||||
@ -16,7 +16,7 @@ export const PopupStartEndInput = ({
|
|||||||
updateEndValue,
|
updateEndValue,
|
||||||
}: StartEndInputProps) => {
|
}: StartEndInputProps) => {
|
||||||
return (
|
return (
|
||||||
<div className='tw-grid tw-grid-cols-2 tw-gap-2 tw-mb-5'>
|
<div className='tw-grid tw-grid-cols-2 tw-gap-2'>
|
||||||
<TextInput
|
<TextInput
|
||||||
type='date'
|
type='date'
|
||||||
placeholder='start'
|
placeholder='start'
|
||||||
|
|||||||
@ -20,7 +20,7 @@ export const PopupTextInput = ({
|
|||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
inputStyle={style}
|
inputStyle={style}
|
||||||
type='text'
|
type='text'
|
||||||
containerStyle={'tw-mt-4 tw-mb-4'}
|
containerStyle={'tw-mt-4'}
|
||||||
></TextInput>
|
></TextInput>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -121,6 +121,20 @@
|
|||||||
width: 24px;
|
width: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flower-icon {
|
||||||
|
position: relative;
|
||||||
|
top: -35px;
|
||||||
|
left: 4px;
|
||||||
|
width: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.network-icon {
|
||||||
|
position: relative;
|
||||||
|
top: -35px;
|
||||||
|
left: 4px;
|
||||||
|
width: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
.leaflet-popup-scrolled {
|
.leaflet-popup-scrolled {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable no-constant-condition */
|
|
||||||
import { useItems, useUpdateItem, useAddItem } from '../Map/hooks/useItems'
|
import { useItems, useUpdateItem, useAddItem } from '../Map/hooks/useItems'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { getValue } from '../../Utils/GetValue'
|
import { getValue } from '../../Utils/GetValue'
|
||||||
@ -14,21 +13,23 @@ import { linkItem, onUpdateItem, unlinkItem } from './itemFunctions'
|
|||||||
import { SimpleForm } from './Templates/SimpleForm'
|
import { SimpleForm } from './Templates/SimpleForm'
|
||||||
import { TabsForm } from './Templates/TabsForm'
|
import { TabsForm } from './Templates/TabsForm'
|
||||||
import { FormHeader } from './Subcomponents/FormHeader'
|
import { FormHeader } from './Subcomponents/FormHeader'
|
||||||
|
import { useAppState } from '../AppShell/hooks/useAppState'
|
||||||
|
import { FlexForm } from './Templates/FlexForm'
|
||||||
|
|
||||||
export function ProfileForm({ userType }: { userType: string }) {
|
export function ProfileForm() {
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
color: '',
|
color: '',
|
||||||
id: '',
|
id: '',
|
||||||
groupType: 'wuerdekompass',
|
group_type: 'wuerdekompass',
|
||||||
status: 'active',
|
status: 'active',
|
||||||
name: '',
|
name: '',
|
||||||
subname: '',
|
subname: '',
|
||||||
text: '',
|
text: '',
|
||||||
contact: '',
|
contact: '',
|
||||||
telephone: '',
|
telephone: '',
|
||||||
nextAppointment: '',
|
next_appointment: '',
|
||||||
image: '',
|
image: '',
|
||||||
markerIcon: '',
|
marker_icon: '',
|
||||||
offers: [] as Tag[],
|
offers: [] as Tag[],
|
||||||
needs: [] as Tag[],
|
needs: [] as Tag[],
|
||||||
relations: [] as Item[],
|
relations: [] as Item[],
|
||||||
@ -50,13 +51,13 @@ export function ProfileForm({ userType }: { userType: string }) {
|
|||||||
const hasUserPermission = useHasUserPermission()
|
const hasUserPermission = useHasUserPermission()
|
||||||
const getItemTags = useGetItemTags()
|
const getItemTags = useGetItemTags()
|
||||||
const items = useItems()
|
const items = useItems()
|
||||||
|
const appState = useAppState()
|
||||||
|
|
||||||
const [urlParams, setUrlParams] = useState(new URLSearchParams(location.search))
|
const [urlParams, setUrlParams] = useState(new URLSearchParams(location.search))
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
item && hasUserPermission('items', 'update', item) && setUpdatePermission(true)
|
item && hasUserPermission('items', 'update', item) && setUpdatePermission(true)
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [hasUserPermission, item])
|
||||||
}, [item])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const itemId = location.pathname.split('/')[2]
|
const itemId = location.pathname.split('/')[2]
|
||||||
@ -64,9 +65,8 @@ export function ProfileForm({ userType }: { userType: string }) {
|
|||||||
const item = items.find((i) => i.id === itemId)
|
const item = items.find((i) => i.id === itemId)
|
||||||
item && setItem(item)
|
item && setItem(item)
|
||||||
|
|
||||||
const layer = layers.find((l) => l.itemType.name === userType)
|
if (!item) {
|
||||||
|
const layer = layers.find((l) => l.itemType.name === appState.userType)
|
||||||
!item &&
|
|
||||||
setItem({
|
setItem({
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
name: user ? user.first_name : '',
|
name: user ? user.first_name : '',
|
||||||
@ -74,6 +74,7 @@ export function ProfileForm({ userType }: { userType: string }) {
|
|||||||
layer,
|
layer,
|
||||||
new: true,
|
new: true,
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [items])
|
}, [items])
|
||||||
@ -107,16 +108,16 @@ export function ProfileForm({ userType }: { userType: string }) {
|
|||||||
setState({
|
setState({
|
||||||
color: newColor,
|
color: newColor,
|
||||||
id: item?.id ?? '',
|
id: item?.id ?? '',
|
||||||
groupType: item?.group_type ?? 'wuerdekompass',
|
group_type: item?.group_type ?? '',
|
||||||
status: item?.status ?? 'active',
|
status: item?.status ?? '',
|
||||||
name: item?.name ?? '',
|
name: item?.name ?? '',
|
||||||
subname: item?.subname ?? '',
|
subname: item?.subname ?? '',
|
||||||
text: item?.text ?? '',
|
text: item?.text ?? '',
|
||||||
contact: item?.contact ?? '',
|
contact: item?.contact ?? '',
|
||||||
telephone: item?.telephone ?? '',
|
telephone: item?.telephone ?? '',
|
||||||
nextAppointment: item?.next_appointment ?? '',
|
next_appointment: item?.next_appointment ?? '',
|
||||||
image: item?.image ?? '',
|
image: item?.image ?? '',
|
||||||
markerIcon: item?.marker_icon ?? '',
|
marker_icon: item?.marker_icon ?? '',
|
||||||
offers,
|
offers,
|
||||||
needs,
|
needs,
|
||||||
relations,
|
relations,
|
||||||
@ -129,8 +130,8 @@ export function ProfileForm({ userType }: { userType: string }) {
|
|||||||
const [template, setTemplate] = useState<string>('')
|
const [template, setTemplate] = useState<string>('')
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTemplate(item.layer?.itemType.template || userType)
|
setTemplate(item.layer?.itemType.template || appState.userType)
|
||||||
}, [userType, item])
|
}, [appState.userType, item])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -147,6 +148,10 @@ export function ProfileForm({ userType }: { userType: string }) {
|
|||||||
|
|
||||||
{template === 'simple' && <SimpleForm state={state} setState={setState}></SimpleForm>}
|
{template === 'simple' && <SimpleForm state={state} setState={setState}></SimpleForm>}
|
||||||
|
|
||||||
|
{template === 'flex' && (
|
||||||
|
<FlexForm item={item} state={state} setState={setState}></FlexForm>
|
||||||
|
)}
|
||||||
|
|
||||||
{template === 'tabs' && (
|
{template === 'tabs' && (
|
||||||
<TabsForm
|
<TabsForm
|
||||||
loading={loading}
|
loading={loading}
|
||||||
@ -160,7 +165,7 @@ export function ProfileForm({ userType }: { userType: string }) {
|
|||||||
></TabsForm>
|
></TabsForm>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className='tw-mt-4 tw-mb-4'>
|
<div className='tw-mt-4'>
|
||||||
<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'}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
@ -177,14 +182,10 @@ export function ProfileForm({ userType }: { userType: string }) {
|
|||||||
urlParams,
|
urlParams,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
style={
|
style={{
|
||||||
true
|
|
||||||
? {
|
|
||||||
backgroundColor: `${item.layer?.itemColorField && getValue(item, item.layer?.itemColorField) ? getValue(item, item.layer?.itemColorField) : getItemTags(item) && getItemTags(item)[0] && getItemTags(item)[0].color ? getItemTags(item)[0].color : item?.layer?.markerDefaultColor}`,
|
backgroundColor: `${item.layer?.itemColorField && getValue(item, item.layer?.itemColorField) ? getValue(item, item.layer?.itemColorField) : getItemTags(item) && getItemTags(item)[0] && getItemTags(item)[0].color ? getItemTags(item)[0].color : item?.layer?.markerDefaultColor}`,
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
}
|
}}
|
||||||
: { color: '#fff' }
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
Update
|
Update
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -16,14 +16,10 @@ import { OnepagerView } from './Templates/OnepagerView'
|
|||||||
import { SimpleView } from './Templates/SimpleView'
|
import { SimpleView } from './Templates/SimpleView'
|
||||||
import { handleDelete, linkItem, unlinkItem } from './itemFunctions'
|
import { handleDelete, linkItem, unlinkItem } from './itemFunctions'
|
||||||
import { useTags } from '../Map/hooks/useTags'
|
import { useTags } from '../Map/hooks/useTags'
|
||||||
|
import { FlexView } from './Templates/FlexView'
|
||||||
|
import { useAppState } from '../AppShell/hooks/useAppState'
|
||||||
|
|
||||||
export function ProfileView({
|
export function ProfileView({ attestationApi }: { attestationApi?: ItemsApi<any> }) {
|
||||||
userType,
|
|
||||||
attestationApi,
|
|
||||||
}: {
|
|
||||||
userType: string
|
|
||||||
attestationApi?: ItemsApi<any>
|
|
||||||
}) {
|
|
||||||
const [item, setItem] = useState<Item>()
|
const [item, setItem] = useState<Item>()
|
||||||
const [updatePermission, setUpdatePermission] = useState<boolean>(false)
|
const [updatePermission, setUpdatePermission] = useState<boolean>(false)
|
||||||
const [relations, setRelations] = useState<Array<Item>>([])
|
const [relations, setRelations] = useState<Array<Item>>([])
|
||||||
@ -44,6 +40,7 @@ export function ProfileView({
|
|||||||
const setSelectPosition = useSetSelectPosition()
|
const setSelectPosition = useSetSelectPosition()
|
||||||
const clusterRef = useClusterRef()
|
const clusterRef = useClusterRef()
|
||||||
const leafletRefs = useLeafletRefs()
|
const leafletRefs = useLeafletRefs()
|
||||||
|
const appState = useAppState()
|
||||||
|
|
||||||
const [attestations, setAttestations] = useState<Array<any>>([])
|
const [attestations, setAttestations] = useState<Array<any>>([])
|
||||||
|
|
||||||
@ -149,8 +146,8 @@ export function ProfileView({
|
|||||||
}, [selectPosition])
|
}, [selectPosition])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTemplate(item?.layer?.itemType.template || userType)
|
setTemplate(item?.layer?.itemType.template || appState.userType)
|
||||||
}, [userType, item])
|
}, [appState.userType, item])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -176,13 +173,14 @@ export function ProfileView({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{template === 'onepager' && <OnepagerView item={item} userType={userType} />}
|
{template === 'onepager' && <OnepagerView item={item} />}
|
||||||
|
|
||||||
{template === 'simple' && <SimpleView item={item} />}
|
{template === 'simple' && <SimpleView item={item} />}
|
||||||
|
|
||||||
|
{template === 'flex' && <FlexView item={item} />}
|
||||||
|
|
||||||
{template === 'tabs' && (
|
{template === 'tabs' && (
|
||||||
<TabsView
|
<TabsView
|
||||||
userType={userType}
|
|
||||||
attestations={attestations}
|
attestations={attestations}
|
||||||
item={item}
|
item={item}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useState, useCallback, useRef } from 'react'
|
import { useState, useCallback, useRef } from 'react'
|
||||||
import ReactCrop, { Crop, centerCrop, makeAspectCrop } from 'react-image-crop'
|
import ReactCrop, { Crop, centerCrop, makeAspectCrop } from 'react-image-crop'
|
||||||
import { useAssetApi } from '../../AppShell/hooks/useAssets'
|
import { useAppState } from '../../AppShell/hooks/useAppState'
|
||||||
import 'react-image-crop/dist/ReactCrop.css'
|
import 'react-image-crop/dist/ReactCrop.css'
|
||||||
import DialogModal from '../../Templates/DialogModal'
|
import DialogModal from '../../Templates/DialogModal'
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ export const AvatarWidget: React.FC<AvatarWidgetProps> = ({ avatar, setAvatar })
|
|||||||
const [cropModalOpen, setCropModalOpen] = useState<boolean>(false)
|
const [cropModalOpen, setCropModalOpen] = useState<boolean>(false)
|
||||||
const [cropping, setCropping] = useState<boolean>(false)
|
const [cropping, setCropping] = useState<boolean>(false)
|
||||||
|
|
||||||
const assetsApi = useAssetApi()
|
const appState = useAppState()
|
||||||
|
|
||||||
const imgRef = useRef<HTMLImageElement>(null)
|
const imgRef = useRef<HTMLImageElement>(null)
|
||||||
|
|
||||||
@ -146,10 +146,10 @@ export const AvatarWidget: React.FC<AvatarWidgetProps> = ({ avatar, setAvatar })
|
|||||||
ctx?.drawImage(img, 0, 0, 400, 400)
|
ctx?.drawImage(img, 0, 0, 400, 400)
|
||||||
|
|
||||||
const resizedBlob = await canvas.convertToBlob()
|
const resizedBlob = await canvas.convertToBlob()
|
||||||
const asset = await assetsApi.upload(resizedBlob, 'avatar')
|
const asset = await appState.assetsApi.upload(resizedBlob, 'avatar')
|
||||||
setAvatar(asset.id)
|
setAvatar(asset.id)
|
||||||
},
|
},
|
||||||
[assetsApi, setAvatar],
|
[appState.assetsApi, setAvatar],
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -180,7 +180,10 @@ export const AvatarWidget: React.FC<AvatarWidgetProps> = ({ avatar, setAvatar })
|
|||||||
</div>
|
</div>
|
||||||
{avatar ? (
|
{avatar ? (
|
||||||
<div className='tw-h-20 tw-w-20'>
|
<div className='tw-h-20 tw-w-20'>
|
||||||
<img src={assetsApi.url + avatar} className='tw-h-20 tw-w-20 tw-rounded-full' />
|
<img
|
||||||
|
src={appState.assetsApi.url + avatar}
|
||||||
|
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'>
|
||||||
|
|||||||
53
src/Components/Profile/Subcomponents/ContactInfoForm.tsx
Normal file
53
src/Components/Profile/Subcomponents/ContactInfoForm.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import * as React from 'react'
|
||||||
|
import { TextInput } from '../../Input'
|
||||||
|
import { FormState } from '../Templates/OnepagerForm'
|
||||||
|
|
||||||
|
export const ContactInfoForm = ({
|
||||||
|
state,
|
||||||
|
setState,
|
||||||
|
}: {
|
||||||
|
state: FormState
|
||||||
|
setState: React.Dispatch<React.SetStateAction<any>>
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className='tw-mt-4 tw-space-y-4'>
|
||||||
|
<div>
|
||||||
|
<label
|
||||||
|
htmlFor='email'
|
||||||
|
className='tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1'
|
||||||
|
>
|
||||||
|
Email-Adresse (Kontakt):
|
||||||
|
</label>
|
||||||
|
<TextInput
|
||||||
|
placeholder='Email'
|
||||||
|
defaultValue={state.contact}
|
||||||
|
updateFormValue={(v) =>
|
||||||
|
setState((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
contact: v,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label
|
||||||
|
htmlFor='telephone'
|
||||||
|
className='tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1'
|
||||||
|
>
|
||||||
|
Telefonnummer (Kontakt):
|
||||||
|
</label>
|
||||||
|
<TextInput
|
||||||
|
placeholder='Telefonnummer'
|
||||||
|
defaultValue={state.telephone}
|
||||||
|
updateFormValue={(v) =>
|
||||||
|
setState((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
telephone: v,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -1,33 +1,46 @@
|
|||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { useAssetApi } from '../../AppShell/hooks/useAssets'
|
import { useAppState } from '../../AppShell/hooks/useAppState'
|
||||||
|
import { Item } from '../../../types'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { useItems } from '../../Map/hooks/useItems'
|
||||||
|
|
||||||
const ContactInfo = ({
|
export const ContactInfoView = ({ item, heading }: { item: Item; heading: string }) => {
|
||||||
email,
|
const appState = useAppState()
|
||||||
telephone,
|
const [profileOwner, setProfileOwner] = useState<Item>()
|
||||||
name,
|
const items = useItems()
|
||||||
avatar,
|
|
||||||
link,
|
useEffect(() => {
|
||||||
}: {
|
console.log(
|
||||||
email: string
|
'user:',
|
||||||
telephone: string
|
items.find(
|
||||||
name: string
|
(i) =>
|
||||||
avatar: string
|
i.user_created?.id === item.user_created?.id &&
|
||||||
link?: string
|
i.layer?.itemType.name === appState.userType,
|
||||||
}) => {
|
),
|
||||||
const assetsApi = useAssetApi()
|
)
|
||||||
|
|
||||||
|
setProfileOwner(
|
||||||
|
items.find(
|
||||||
|
(i) =>
|
||||||
|
i.user_created?.id === item.user_created?.id &&
|
||||||
|
i.layer?.itemType.name === appState.userType,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [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'>Du hast Fragen?</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'>
|
||||||
{avatar && (
|
{profileOwner?.image && (
|
||||||
<ConditionalLink url={link}>
|
<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={assetsApi.url + avatar}
|
src={appState.assetsApi.url + profileOwner?.image}
|
||||||
alt={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>
|
||||||
@ -36,11 +49,11 @@ const ContactInfo = ({
|
|||||||
</ConditionalLink>
|
</ConditionalLink>
|
||||||
)}
|
)}
|
||||||
<div className='tw-text-sm tw-flex-grow'>
|
<div className='tw-text-sm tw-flex-grow'>
|
||||||
<p className='tw-font-semibold'>{name}</p>
|
<p className='tw-font-semibold'>{profileOwner?.name}</p>
|
||||||
{email && (
|
{item.contact && (
|
||||||
<p>
|
<p>
|
||||||
<a
|
<a
|
||||||
href={`mailto:${email}`}
|
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'
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
@ -56,14 +69,14 @@ const ContactInfo = ({
|
|||||||
<path d='M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z'></path>
|
<path d='M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z'></path>
|
||||||
<polyline points='22,6 12,13 2,6'></polyline>
|
<polyline points='22,6 12,13 2,6'></polyline>
|
||||||
</svg>
|
</svg>
|
||||||
{email}
|
{item.contact}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{telephone && (
|
{item.telephone && (
|
||||||
<p>
|
<p>
|
||||||
<a
|
<a
|
||||||
href={`tel:${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'
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
@ -78,7 +91,7 @@ const ContactInfo = ({
|
|||||||
>
|
>
|
||||||
<path d='M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07 19.5 19.5 0 01-6-6 19.79 19.79 0 01-3.07-8.67A2 2 0 014.11 2h3a2 2 0 012 1.72 12.84 12.84 0 00.7 2.81 2 2 0 01-.45 2.11L8.09 9.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45 12.84 12.84 0 002.81.7A2 2 0 0122 16.92z' />
|
<path d='M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07 19.5 19.5 0 01-6-6 19.79 19.79 0 01-3.07-8.67A2 2 0 014.11 2h3a2 2 0 012 1.72 12.84 12.84 0 00.7 2.81 2 2 0 01-.45 2.11L8.09 9.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45 12.84 12.84 0 002.81.7A2 2 0 0122 16.92z' />
|
||||||
</svg>
|
</svg>
|
||||||
{telephone}
|
{item.telephone}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
@ -88,8 +101,6 @@ const ContactInfo = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ContactInfo
|
|
||||||
|
|
||||||
// eslint-disable-next-line react/prop-types
|
// eslint-disable-next-line react/prop-types
|
||||||
const ConditionalLink = ({ url, children }) => {
|
const ConditionalLink = ({ url, children }) => {
|
||||||
const params = new URLSearchParams(window.location.search)
|
const params = new URLSearchParams(window.location.search)
|
||||||
33
src/Components/Profile/Subcomponents/GroupSubHeaderView.tsx
Normal file
33
src/Components/Profile/Subcomponents/GroupSubHeaderView.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/* eslint-disable camelcase */
|
||||||
|
import { Item } from '../../../types'
|
||||||
|
import SocialShareBar from './SocialShareBar'
|
||||||
|
|
||||||
|
export const GroupSubHeaderView = ({
|
||||||
|
item,
|
||||||
|
share_base_url,
|
||||||
|
}: {
|
||||||
|
item: Item
|
||||||
|
share_base_url: string
|
||||||
|
}) => (
|
||||||
|
<div className='tw-px-6'>
|
||||||
|
<div className='tw-float-left tw-mt-2 tw-mb-4 tw-flex tw-items-center'>
|
||||||
|
{item.status && (
|
||||||
|
<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'>
|
||||||
|
{item.status}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{item.group_type && (
|
||||||
|
<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'>
|
||||||
|
{item.group_type}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<SocialShareBar url={share_base_url + item.slug} title={item.name} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
87
src/Components/Profile/Subcomponents/GroupSubheaderForm.tsx
Normal file
87
src/Components/Profile/Subcomponents/GroupSubheaderForm.tsx
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import * as React from 'react'
|
||||||
|
import ComboBoxInput from '../../Input/ComboBoxInput'
|
||||||
|
import { Item } from '../../../types'
|
||||||
|
import { useEffect } from 'react'
|
||||||
|
import { FormState } from '../Templates/OnepagerForm'
|
||||||
|
|
||||||
|
type groupType = {
|
||||||
|
groupTypes_id: {
|
||||||
|
name: string
|
||||||
|
color: string
|
||||||
|
image: string
|
||||||
|
markerIcon: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GroupSubheaderForm = ({
|
||||||
|
state,
|
||||||
|
setState,
|
||||||
|
groupStates,
|
||||||
|
groupTypes,
|
||||||
|
}: {
|
||||||
|
state: FormState
|
||||||
|
setState: React.Dispatch<React.SetStateAction<any>>
|
||||||
|
item: Item
|
||||||
|
groupStates?: string[]
|
||||||
|
groupTypes?: groupType[]
|
||||||
|
}) => {
|
||||||
|
useEffect(() => {
|
||||||
|
if (groupTypes && groupStates) {
|
||||||
|
const groupType = groupTypes.find((gt) => gt.groupTypes_id.name === state.group_type)
|
||||||
|
console.log(state.group_type)
|
||||||
|
setState((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
color: groupType?.groupTypes_id.color || groupTypes[0].groupTypes_id.color,
|
||||||
|
marker_icon: groupType?.groupTypes_id.markerIcon || groupTypes[0].groupTypes_id.markerIcon,
|
||||||
|
image: groupType?.groupTypes_id.image || groupTypes[0].groupTypes_id.image,
|
||||||
|
status: state.status || groupStates[0],
|
||||||
|
group_type: state.group_type || groupTypes[0].groupTypes_id.name,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [state.group_type, groupTypes])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='tw-grid tw-grid-cols-1 md:tw-grid-cols-2 tw-gap-6'>
|
||||||
|
<div>
|
||||||
|
<label
|
||||||
|
htmlFor='status'
|
||||||
|
className='tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1'
|
||||||
|
>
|
||||||
|
Gruppenstatus:
|
||||||
|
</label>
|
||||||
|
<ComboBoxInput
|
||||||
|
id='status'
|
||||||
|
options={groupStates || []}
|
||||||
|
value={state.status}
|
||||||
|
onValueChange={(v) =>
|
||||||
|
setState((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
status: v,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label
|
||||||
|
htmlFor='groupType'
|
||||||
|
className='tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1'
|
||||||
|
>
|
||||||
|
Gruppenart:
|
||||||
|
</label>
|
||||||
|
<ComboBoxInput
|
||||||
|
id='groupType'
|
||||||
|
options={groupTypes?.map((gt) => gt.groupTypes_id.name) || []}
|
||||||
|
value={state.group_type}
|
||||||
|
onValueChange={(v) =>
|
||||||
|
setState((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
group_type: v,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { getValue } from '../../../Utils/GetValue'
|
import { getValue } from '../../../Utils/GetValue'
|
||||||
import { Item } from '../../../types'
|
import { Item } from '../../../types'
|
||||||
import { useAssetApi } from '../../AppShell/hooks/useAssets'
|
import { useAppState } from '../../AppShell/hooks/useAppState'
|
||||||
|
|
||||||
export function LinkedItemsHeaderView({
|
export function LinkedItemsHeaderView({
|
||||||
item,
|
item,
|
||||||
@ -20,15 +20,15 @@ export function LinkedItemsHeaderView({
|
|||||||
loading?: boolean
|
loading?: boolean
|
||||||
unlinkPermission: boolean
|
unlinkPermission: boolean
|
||||||
}) {
|
}) {
|
||||||
const assetsApi = useAssetApi()
|
const appState = useAppState()
|
||||||
|
|
||||||
const avatar =
|
const avatar =
|
||||||
itemAvatarField && getValue(item, itemAvatarField)
|
itemAvatarField && getValue(item, itemAvatarField)
|
||||||
? assetsApi.url + getValue(item, itemAvatarField)
|
? appState.assetsApi.url + getValue(item, itemAvatarField)
|
||||||
: item.layer?.itemAvatarField &&
|
: item.layer?.itemAvatarField &&
|
||||||
item &&
|
item &&
|
||||||
getValue(item, item.layer?.itemAvatarField) &&
|
getValue(item, item.layer?.itemAvatarField) &&
|
||||||
assetsApi.url + getValue(item, item.layer?.itemAvatarField)
|
appState.assetsApi.url + getValue(item, item.layer?.itemAvatarField)
|
||||||
const title = itemNameField
|
const title = itemNameField
|
||||||
? getValue(item, itemNameField)
|
? getValue(item, itemNameField)
|
||||||
: item.layer?.itemNameField && item && getValue(item, item.layer?.itemNameField)
|
: item.layer?.itemNameField && item && getValue(item, item.layer?.itemNameField)
|
||||||
|
|||||||
30
src/Components/Profile/Subcomponents/ProfileStartEndForm.tsx
Normal file
30
src/Components/Profile/Subcomponents/ProfileStartEndForm.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import * as React from 'react'
|
||||||
|
import { PopupStartEndInput } from '../../Map'
|
||||||
|
import { Item } from '../../../types'
|
||||||
|
|
||||||
|
export const ProfileStartEndForm = ({
|
||||||
|
item,
|
||||||
|
setState,
|
||||||
|
}: {
|
||||||
|
item: Item
|
||||||
|
setState: React.Dispatch<React.SetStateAction<any>>
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<PopupStartEndInput
|
||||||
|
item={item}
|
||||||
|
showLabels={false}
|
||||||
|
updateEndValue={(e) =>
|
||||||
|
setState((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
end: e,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
updateStartValue={(s) =>
|
||||||
|
setState((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
start: s,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
></PopupStartEndInput>
|
||||||
|
)
|
||||||
|
}
|
||||||
11
src/Components/Profile/Subcomponents/ProfileStartEndView.tsx
Normal file
11
src/Components/Profile/Subcomponents/ProfileStartEndView.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import * as React from 'react'
|
||||||
|
import { StartEndView } from '../../Map'
|
||||||
|
import { Item } from '../../../types'
|
||||||
|
|
||||||
|
export const ProfileStartEndView = ({ item }: { item: Item }) => {
|
||||||
|
return (
|
||||||
|
<div className='tw-mt-2 tw-px-6 tw-max-w-xs'>
|
||||||
|
<StartEndView item={item}></StartEndView>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -1,54 +0,0 @@
|
|||||||
import SocialShareBar from './SocialShareBar'
|
|
||||||
|
|
||||||
/* const flags = {
|
|
||||||
de: (
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5 3" className="tw-w-5 tw-h-3">
|
|
||||||
<rect width="5" height="3" fill="#FFCE00" />
|
|
||||||
<rect width="5" height="2" fill="#DD0000" />
|
|
||||||
<rect width="5" height="1" fill="#000000" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
at: (
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5 3" className="tw-w-5 tw-h-3">
|
|
||||||
<rect width="5" height="3" fill="#ED2939" />
|
|
||||||
<rect width="5" height="2" fill="#FFFFFF" />
|
|
||||||
<rect width="5" height="1" fill="#ED2939" />
|
|
||||||
</svg>
|
|
||||||
)
|
|
||||||
}; */
|
|
||||||
|
|
||||||
const statusMapping = {
|
|
||||||
in_planning: 'in Planung',
|
|
||||||
paused: 'pausiert',
|
|
||||||
active: 'aktiv',
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line react/prop-types
|
|
||||||
const SubHeader = ({ type, status, url, title }) => (
|
|
||||||
<div>
|
|
||||||
<div className='tw-float-left tw-mt-2 tw-mb-4 tw-flex tw-items-center'>
|
|
||||||
{status && (
|
|
||||||
<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-w-2 tw-h-2 ${status === 'in_planning' && 'tw-bg-blue-700'} ${status === 'paused' && 'tw-bg-orange-400'} ${status === 'active' && 'tw-bg-green-500'} tw-rounded-full tw-mr-1.5`}
|
|
||||||
></span>
|
|
||||||
{statusMapping[status]}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{type && (
|
|
||||||
<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'>
|
|
||||||
{type}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<SocialShareBar url={url} title={title} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
export default SubHeader
|
|
||||||
53
src/Components/Profile/Subcomponents/ProfileTextForm.tsx
Normal file
53
src/Components/Profile/Subcomponents/ProfileTextForm.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import * as React from 'react'
|
||||||
|
import { TextAreaInput } from '../../Input'
|
||||||
|
import { FormState } from '../Templates/OnepagerForm'
|
||||||
|
import { getValue } from '../../../Utils/GetValue'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
export const ProfileTextForm = ({
|
||||||
|
state,
|
||||||
|
setState,
|
||||||
|
dataField,
|
||||||
|
heading,
|
||||||
|
size,
|
||||||
|
hideInputLabel,
|
||||||
|
}: {
|
||||||
|
state: FormState
|
||||||
|
setState: React.Dispatch<React.SetStateAction<any>>
|
||||||
|
dataField?: string
|
||||||
|
heading: string
|
||||||
|
size: string
|
||||||
|
hideInputLabel: boolean
|
||||||
|
}) => {
|
||||||
|
const [field, setField] = useState<string>(dataField || 'text')
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!dataField) {
|
||||||
|
setField('text')
|
||||||
|
}
|
||||||
|
}, [dataField])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='tw-h-full tw-flex tw-flex-col tw-mt-4'>
|
||||||
|
<label
|
||||||
|
htmlFor='nextAppointment'
|
||||||
|
className='tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1'
|
||||||
|
>
|
||||||
|
{heading || 'Text'}:
|
||||||
|
</label>
|
||||||
|
<TextAreaInput
|
||||||
|
placeholder={'...'}
|
||||||
|
defaultValue={getValue(state, field)}
|
||||||
|
updateFormValue={(v) =>
|
||||||
|
setState((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
[field]: v,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
labelStyle={hideInputLabel ? 'tw-hidden' : ''}
|
||||||
|
containerStyle={size === 'full' ? 'tw-grow tw-h-full' : ''}
|
||||||
|
inputStyle={size === 'full' ? 'tw-h-full' : 'tw-h-24'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
26
src/Components/Profile/Subcomponents/ProfileTextView.tsx
Normal file
26
src/Components/Profile/Subcomponents/ProfileTextView.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { Item } from '../../../types'
|
||||||
|
import { getValue } from '../../../Utils/GetValue'
|
||||||
|
import { TextView } from '../../Map'
|
||||||
|
|
||||||
|
export const ProfileTextView = ({
|
||||||
|
item,
|
||||||
|
dataField,
|
||||||
|
heading,
|
||||||
|
hideWhenEmpty,
|
||||||
|
}: {
|
||||||
|
item: Item
|
||||||
|
dataField: string
|
||||||
|
heading: string
|
||||||
|
hideWhenEmpty: boolean
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className='tw-my-10 tw-mt-2 tw-px-6'>
|
||||||
|
{!(getValue(item, dataField) === '' && hideWhenEmpty) && (
|
||||||
|
<h2 className='tw-text-lg tw-font-semibold'>{heading}</h2>
|
||||||
|
)}
|
||||||
|
<div className='tw-mt-2 tw-text-sm'>
|
||||||
|
<TextView rawText={dataField ? getValue(item, dataField) : getValue(item, 'text')} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
44
src/Components/Profile/Templates/FlexForm.tsx
Normal file
44
src/Components/Profile/Templates/FlexForm.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import * as React from 'react'
|
||||||
|
import { Item } from '../../../types'
|
||||||
|
import { FormState } from './OnepagerForm'
|
||||||
|
import { GroupSubheaderForm } from '../Subcomponents/GroupSubheaderForm'
|
||||||
|
import { ContactInfoForm } from '../Subcomponents/ContactInfoForm'
|
||||||
|
import { ProfileTextForm } from '../Subcomponents/ProfileTextForm'
|
||||||
|
import { ProfileStartEndForm } from '../Subcomponents/ProfileStartEndForm'
|
||||||
|
|
||||||
|
const componentMap = {
|
||||||
|
groupSubheaders: GroupSubheaderForm,
|
||||||
|
texts: ProfileTextForm,
|
||||||
|
contactInfos: ContactInfoForm,
|
||||||
|
startEnd: ProfileStartEndForm,
|
||||||
|
// weitere Komponenten hier
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FlexForm = ({
|
||||||
|
item,
|
||||||
|
state,
|
||||||
|
setState,
|
||||||
|
}: {
|
||||||
|
state: FormState
|
||||||
|
setState: React.Dispatch<React.SetStateAction<any>>
|
||||||
|
item: Item
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className='tw-mt-6 tw-flex tw-flex-col tw-h-full'>
|
||||||
|
{item.layer?.itemType.profileTemplate.map((templateItem) => {
|
||||||
|
const TemplateComponent = componentMap[templateItem.collection]
|
||||||
|
return TemplateComponent ? (
|
||||||
|
<TemplateComponent
|
||||||
|
key={templateItem.id}
|
||||||
|
state={state}
|
||||||
|
setState={setState}
|
||||||
|
item={item}
|
||||||
|
{...templateItem.item}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div key={templateItem.id}>Component not found</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
29
src/Components/Profile/Templates/FlexView.tsx
Normal file
29
src/Components/Profile/Templates/FlexView.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { GroupSubHeaderView } from '../Subcomponents/GroupSubHeaderView'
|
||||||
|
import { ProfileTextView } from '../Subcomponents/ProfileTextView'
|
||||||
|
import { ContactInfoView } from '../Subcomponents/ContactInfoView'
|
||||||
|
import { Item } from '../../../types'
|
||||||
|
import { ProfileStartEndView } from '../Subcomponents/ProfileStartEndView'
|
||||||
|
|
||||||
|
const componentMap = {
|
||||||
|
groupSubheaders: GroupSubHeaderView,
|
||||||
|
texts: ProfileTextView,
|
||||||
|
contactInfos: ContactInfoView,
|
||||||
|
startEnd: ProfileStartEndView,
|
||||||
|
// weitere Komponenten hier
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FlexView = ({ item }: { item: Item }) => {
|
||||||
|
console.log(item)
|
||||||
|
return (
|
||||||
|
<div className='tw-h-full tw-overflow-y-auto fade'>
|
||||||
|
{item.layer?.itemType.profileTemplate.map((templateItem) => {
|
||||||
|
const TemplateComponent = componentMap[templateItem.collection]
|
||||||
|
return TemplateComponent ? (
|
||||||
|
<TemplateComponent key={templateItem.id} item={item} {...templateItem.item} />
|
||||||
|
) : (
|
||||||
|
<div key={templateItem.id}>Component not found</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -1,178 +1,40 @@
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useEffect } from 'react'
|
|
||||||
import { Item, Tag } from '../../../types'
|
import { Item, Tag } from '../../../types'
|
||||||
import { TextAreaInput, TextInput } from '../../Input'
|
import { TextAreaInput } from '../../Input'
|
||||||
import ComboBoxInput from '../../Input/ComboBoxInput'
|
import { GroupSubheaderForm } from '../Subcomponents/GroupSubheaderForm'
|
||||||
|
import { ContactInfoForm } from '../Subcomponents/ContactInfoForm'
|
||||||
|
|
||||||
export const OnepagerForm = ({
|
export type FormState = {
|
||||||
item,
|
|
||||||
state,
|
|
||||||
setState,
|
|
||||||
}: {
|
|
||||||
state: {
|
|
||||||
color: string
|
color: string
|
||||||
id: string
|
id: string
|
||||||
groupType: string
|
group_type: string
|
||||||
status: string
|
status: string
|
||||||
name: string
|
name: string
|
||||||
subname: string
|
subname: string
|
||||||
text: string
|
text: string
|
||||||
contact: string
|
contact: string
|
||||||
telephone: string
|
telephone: string
|
||||||
nextAppointment: string
|
next_appointment: string
|
||||||
image: string
|
image: string
|
||||||
markerIcon: string
|
marker_icon: string
|
||||||
offers: Tag[]
|
offers: Tag[]
|
||||||
needs: Tag[]
|
needs: Tag[]
|
||||||
relations: Item[]
|
relations: Item[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const OnepagerForm = ({
|
||||||
|
item,
|
||||||
|
state,
|
||||||
|
setState,
|
||||||
|
}: {
|
||||||
|
state: FormState
|
||||||
setState: React.Dispatch<React.SetStateAction<any>>
|
setState: React.Dispatch<React.SetStateAction<any>>
|
||||||
item: Item
|
item: Item
|
||||||
}) => {
|
}) => {
|
||||||
useEffect(() => {
|
|
||||||
switch (state.groupType) {
|
|
||||||
case 'wuerdekompass':
|
|
||||||
setState((prevState) => ({
|
|
||||||
...prevState,
|
|
||||||
color: item?.layer?.menuColor || '#1A5FB4',
|
|
||||||
markerIcon: 'group',
|
|
||||||
image: '59e6a346-d1ee-4767-9e42-fc720fb535c9',
|
|
||||||
}))
|
|
||||||
break
|
|
||||||
case 'themenkompass':
|
|
||||||
setState((prevState) => ({
|
|
||||||
...prevState,
|
|
||||||
color: '#26A269',
|
|
||||||
markerIcon: 'group',
|
|
||||||
image: '59e6a346-d1ee-4767-9e42-fc720fb535c9',
|
|
||||||
}))
|
|
||||||
break
|
|
||||||
case 'liebevoll.jetzt':
|
|
||||||
setState((prevState) => ({
|
|
||||||
...prevState,
|
|
||||||
color: '#E8B620',
|
|
||||||
markerIcon: 'liebevoll.jetzt',
|
|
||||||
image: 'e735b96c-507b-471c-8317-386ece0ca51d',
|
|
||||||
}))
|
|
||||||
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [state.groupType])
|
|
||||||
|
|
||||||
const typeMapping = [
|
|
||||||
{ value: 'wuerdekompass', label: 'Regional-Gruppe' },
|
|
||||||
{ value: 'themenkompass', label: 'Themen-Gruppe' },
|
|
||||||
{ value: 'liebevoll.jetzt', label: 'liebevoll.jetzt' },
|
|
||||||
]
|
|
||||||
const statusMapping = [
|
|
||||||
{ value: 'active', label: 'aktiv' },
|
|
||||||
{ value: 'in_planning', label: 'in Planung' },
|
|
||||||
{ value: 'paused', label: 'pausiert' },
|
|
||||||
]
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='tw-space-y-6 tw-mt-6'>
|
<div className='tw-space-y-6 tw-mt-6'>
|
||||||
<div className='tw-grid tw-grid-cols-1 md:tw-grid-cols-2 tw-gap-6'>
|
<GroupSubheaderForm state={state} setState={setState} item={item}></GroupSubheaderForm>
|
||||||
<div>
|
<ContactInfoForm state={state} setState={setState}></ContactInfoForm>
|
||||||
<label
|
|
||||||
htmlFor='groupType'
|
|
||||||
className='tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1'
|
|
||||||
>
|
|
||||||
Gruppenart:
|
|
||||||
</label>
|
|
||||||
<ComboBoxInput
|
|
||||||
id='groupType'
|
|
||||||
options={typeMapping}
|
|
||||||
value={state.groupType}
|
|
||||||
onValueChange={(v) =>
|
|
||||||
setState((prevState) => ({
|
|
||||||
...prevState,
|
|
||||||
groupType: v,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label
|
|
||||||
htmlFor='status'
|
|
||||||
className='tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1'
|
|
||||||
>
|
|
||||||
Gruppenstatus:
|
|
||||||
</label>
|
|
||||||
<ComboBoxInput
|
|
||||||
id='status'
|
|
||||||
options={statusMapping}
|
|
||||||
value={state.status}
|
|
||||||
onValueChange={(v) =>
|
|
||||||
setState((prevState) => ({
|
|
||||||
...prevState,
|
|
||||||
status: v,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label
|
|
||||||
htmlFor='email'
|
|
||||||
className='tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1'
|
|
||||||
>
|
|
||||||
Email-Adresse (Kontakt):
|
|
||||||
</label>
|
|
||||||
<TextInput
|
|
||||||
placeholder='Email'
|
|
||||||
defaultValue={state.contact}
|
|
||||||
updateFormValue={(v) =>
|
|
||||||
setState((prevState) => ({
|
|
||||||
...prevState,
|
|
||||||
contact: v,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label
|
|
||||||
htmlFor='telephone'
|
|
||||||
className='tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1'
|
|
||||||
>
|
|
||||||
Telefonnummer (Kontakt):
|
|
||||||
</label>
|
|
||||||
<TextInput
|
|
||||||
placeholder='Telefonnummer'
|
|
||||||
defaultValue={state.telephone}
|
|
||||||
updateFormValue={(v) =>
|
|
||||||
setState((prevState) => ({
|
|
||||||
...prevState,
|
|
||||||
telephone: v,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label
|
|
||||||
htmlFor='nextAppointment'
|
|
||||||
className='tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1'
|
|
||||||
>
|
|
||||||
Nächste Termine:
|
|
||||||
</label>
|
|
||||||
<TextAreaInput
|
|
||||||
placeholder='Nächste Termine'
|
|
||||||
defaultValue={state.nextAppointment}
|
|
||||||
updateFormValue={(v) =>
|
|
||||||
setState((prevState) => ({
|
|
||||||
...prevState,
|
|
||||||
nextAppointment: v,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
inputStyle='tw-h-24'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label
|
<label
|
||||||
|
|||||||
@ -1,51 +1,16 @@
|
|||||||
import { Item } from '../../../types'
|
import { Item } from '../../../types'
|
||||||
import { TextView } from '../../Map'
|
import { TextView } from '../../Map'
|
||||||
import ContactInfo from '../Subcomponents/ContactInfo'
|
import { ContactInfoView } from '../Subcomponents/ContactInfoView'
|
||||||
import ProfileSubHeader from '../Subcomponents/ProfileSubHeader'
|
import { GroupSubHeaderView } from '../Subcomponents/GroupSubHeaderView'
|
||||||
import { useEffect, useState } from 'react'
|
|
||||||
import { useItems } from '../../Map/hooks/useItems'
|
|
||||||
|
|
||||||
export const OnepagerView = ({ item, userType }: { item: Item; userType: string }) => {
|
|
||||||
const [profileOwner, setProfileOwner] = useState<Item>()
|
|
||||||
const items = useItems()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setProfileOwner(
|
|
||||||
items.find(
|
|
||||||
(i) => i.user_created?.id === item.user_created?.id && i.layer?.itemType.name === userType,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [item, items])
|
|
||||||
|
|
||||||
const typeMapping = {
|
|
||||||
wuerdekompass: 'Regional-Gruppe',
|
|
||||||
themenkompass: 'Themenkompass-Gruppe',
|
|
||||||
'liebevoll.jetzt': 'liebevoll.jetzt',
|
|
||||||
}
|
|
||||||
|
|
||||||
const groupType = item.group_type ? item.group_type : 'default'
|
|
||||||
const groupTypeText = typeMapping[groupType]
|
|
||||||
|
|
||||||
|
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'>
|
||||||
<div className='tw-px-6'>
|
<GroupSubHeaderView
|
||||||
<ProfileSubHeader
|
item={item}
|
||||||
type={groupTypeText}
|
share_base_url={`https://www.wuerdekompass.org/aktivitaeten/gruppensuche/#/gruppe/${item.slug}`}
|
||||||
status={item.status}
|
|
||||||
url={`https://www.wuerdekompass.org/aktivitaeten/gruppensuche/#/gruppe/${item.slug}`}
|
|
||||||
title={item.name}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
{item.user_created.first_name && <ContactInfoView heading='Du hast Fragen?' item={item} />}
|
||||||
{item.user_created.first_name && (
|
|
||||||
<ContactInfo
|
|
||||||
link={`/item/${profileOwner?.id}`}
|
|
||||||
name={profileOwner?.name ? profileOwner.name : item.user_created.first_name}
|
|
||||||
avatar={profileOwner?.image ? profileOwner.image : item.user_created.avatar}
|
|
||||||
email={item.contact}
|
|
||||||
telephone={item.telephone}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{/* 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 rawText={item.text || 'Keine Beschreibung vorhanden'} />
|
<TextView rawText={item.text || 'Keine Beschreibung vorhanden'} />
|
||||||
|
|||||||
@ -7,12 +7,11 @@ import { useAddFilterTag } from '../../Map/hooks/useFilter'
|
|||||||
import { Item, Tag } from '../../../types'
|
import { Item, Tag } from '../../../types'
|
||||||
import { Link, useNavigate } from 'react-router-dom'
|
import { Link, useNavigate } from 'react-router-dom'
|
||||||
import { useItems } from '../../Map/hooks/useItems'
|
import { useItems } from '../../Map/hooks/useItems'
|
||||||
import { useAssetApi } from '../../AppShell/hooks/useAssets'
|
import { useAppState } from '../../AppShell/hooks/useAppState'
|
||||||
import { timeAgo } from '../../../Utils/TimeAgo'
|
import { timeAgo } from '../../../Utils/TimeAgo'
|
||||||
|
|
||||||
export const TabsView = ({
|
export const TabsView = ({
|
||||||
attestations,
|
attestations,
|
||||||
userType,
|
|
||||||
item,
|
item,
|
||||||
offers,
|
offers,
|
||||||
needs,
|
needs,
|
||||||
@ -23,7 +22,6 @@ export const TabsView = ({
|
|||||||
unlinkItem,
|
unlinkItem,
|
||||||
}: {
|
}: {
|
||||||
attestations: Array<any>
|
attestations: Array<any>
|
||||||
userType: string
|
|
||||||
item: Item
|
item: Item
|
||||||
offers: Array<Tag>
|
offers: Array<Tag>
|
||||||
needs: Array<Tag>
|
needs: Array<Tag>
|
||||||
@ -40,9 +38,11 @@ export const TabsView = ({
|
|||||||
const [addItemPopupType] = useState<string>('')
|
const [addItemPopupType] = useState<string>('')
|
||||||
|
|
||||||
const items = useItems()
|
const items = useItems()
|
||||||
const assetsApi = useAssetApi()
|
const appState = useAppState()
|
||||||
const getUserProfile = (id: string) => {
|
const getUserProfile = (id: string) => {
|
||||||
return items.find((i) => i.user_created.id === id && i.layer?.itemType.name === userType)
|
return items.find(
|
||||||
|
(i) => i.user_created.id === id && i.layer?.itemType.name === appState.userType,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -146,7 +146,10 @@ export const TabsView = ({
|
|||||||
<div className='tw-avatar'>
|
<div className='tw-avatar'>
|
||||||
<div className='tw-mask tw-rounded-full h-8 w-8 tw-mr-2'>
|
<div className='tw-mask tw-rounded-full h-8 w-8 tw-mr-2'>
|
||||||
<img
|
<img
|
||||||
src={assetsApi.url + getUserProfile(a.user_created.id)?.image}
|
src={
|
||||||
|
appState.assetsApi.url +
|
||||||
|
getUserProfile(a.user_created.id)?.image
|
||||||
|
}
|
||||||
alt='Avatar Tailwind CSS Component'
|
alt='Avatar Tailwind CSS Component'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -168,14 +168,14 @@ export const onUpdateItem = async (
|
|||||||
text: state.text,
|
text: state.text,
|
||||||
...(state.color && { color: state.color }),
|
...(state.color && { color: state.color }),
|
||||||
position: item.position,
|
position: item.position,
|
||||||
...(state.groupType && { group_type: state.groupType }),
|
...(state.group_type && { group_type: state.group_type }),
|
||||||
...(state.status && { status: state.status }),
|
...(state.status && { status: state.status }),
|
||||||
contact: state.contact,
|
contact: state.contact,
|
||||||
telephone: state.telephone,
|
telephone: state.telephone,
|
||||||
...(state.end && { end: state.end }),
|
...(state.end && { end: state.end }),
|
||||||
...(state.start && { start: state.start }),
|
...(state.start && { start: state.start }),
|
||||||
...(state.markerIcon && { markerIcon: state.markerIcon }),
|
...(state.marker_icon && { markerIcon: state.marker_icon }),
|
||||||
next_appointment: state.nextAppointment,
|
next_appointment: state.next_appointment,
|
||||||
...(state.image.length > 10 && { image: state.image }),
|
...(state.image.length > 10 && { image: state.image }),
|
||||||
...(state.offers.length > 0 && { offers: offerUpdates }),
|
...(state.offers.length > 0 && { offers: offerUpdates }),
|
||||||
...(state.needs.length > 0 && { needs: needsUpdates }),
|
...(state.needs.length > 0 && { needs: needsUpdates }),
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { MapOverlayPage } from './MapOverlayPage'
|
import { MapOverlayPage } from './MapOverlayPage'
|
||||||
import { useItems } from '../Map/hooks/useItems'
|
import { useItems } from '../Map/hooks/useItems'
|
||||||
import { useAssetApi } from '../AppShell/hooks/useAssets'
|
import { useAppState } from '../AppShell/hooks/useAppState'
|
||||||
import { EmojiPicker } from './EmojiPicker'
|
import { EmojiPicker } from './EmojiPicker'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { useRef, useState, useEffect } from 'react'
|
import { useRef, useState, useEffect } from 'react'
|
||||||
@ -10,7 +10,7 @@ import { toast } from 'react-toastify'
|
|||||||
|
|
||||||
export const AttestationForm = ({ api }: { api?: ItemsApi<any> }) => {
|
export const AttestationForm = ({ api }: { api?: ItemsApi<any> }) => {
|
||||||
const items = useItems()
|
const items = useItems()
|
||||||
const assetsApi = useAssetApi()
|
const appState = useAppState()
|
||||||
const [users, setUsers] = useState<Array<Item>>()
|
const [users, setUsers] = useState<Array<Item>>()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
@ -88,7 +88,10 @@ export const AttestationForm = ({ api }: { api?: ItemsApi<any> }) => {
|
|||||||
{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 src={assetsApi.url + u.image + '?width=40&heigth=40'} alt='Avatar' />
|
<img
|
||||||
|
src={appState.assetsApi.url + u.image + '?width=40&heigth=40'}
|
||||||
|
alt='Avatar'
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { MapOverlayPage } from './MapOverlayPage'
|
import { MapOverlayPage } from './MapOverlayPage'
|
||||||
import { useItems } from '../Map/hooks/useItems'
|
import { useItems } from '../Map/hooks/useItems'
|
||||||
import { useAssetApi } from '../AppShell/hooks/useAssets'
|
import { useAppState } from '../AppShell/hooks/useAppState'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
export const SelectUser = ({ userType }: { userType: string }) => {
|
export const SelectUser = () => {
|
||||||
|
const appState = useAppState()
|
||||||
const items = useItems()
|
const items = useItems()
|
||||||
const users = items.filter((i) => i.layer?.itemType.name === userType)
|
const users = items.filter((i) => i.layer?.itemType.name === appState.userType)
|
||||||
const assetsApi = useAssetApi()
|
|
||||||
|
|
||||||
const [selectedUsers, setSelectedUsers] = useState<Array<string>>([])
|
const [selectedUsers, setSelectedUsers] = useState<Array<string>>([])
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ export const SelectUser = ({ userType }: { userType: string }) => {
|
|||||||
<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={assetsApi.url + u.image + '?width=40&heigth=40'}
|
src={appState.assetsApi.url + u.image + '?width=40&heigth=40'}
|
||||||
alt='Avatar'
|
alt='Avatar'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -54,6 +54,10 @@ const addIcon = (icon: string) => {
|
|||||||
return '<svg class="group-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#fff" width="1.6em" height="1.6em"><path d="M11.25 5.337c0-.355-.186-.676-.401-.959a1.647 1.647 0 0 1-.349-1.003c0-1.036 1.007-1.875 2.25-1.875S15 2.34 15 3.375c0 .369-.128.713-.349 1.003-.215.283-.401.604-.401.959 0 .332.278.598.61.578 1.91-.114 3.79-.342 5.632-.676a.75.75 0 0 1 .878.645 49.17 49.17 0 0 1 .376 5.452.657.657 0 0 1-.66.664c-.354 0-.675-.186-.958-.401a1.647 1.647 0 0 0-1.003-.349c-1.035 0-1.875 1.007-1.875 2.25s.84 2.25 1.875 2.25c.369 0 .713-.128 1.003-.349.283-.215.604-.401.959-.401.31 0 .557.262.534.571a48.774 48.774 0 0 1-.595 4.845.75.75 0 0 1-.61.61c-1.82.317-3.673.533-5.555.642a.58.58 0 0 1-.611-.581c0-.355.186-.676.401-.959.221-.29.349-.634.349-1.003 0-1.035-1.007-1.875-2.25-1.875s-2.25.84-2.25 1.875c0 .369.128.713.349 1.003.215.283.401.604.401.959a.641.641 0 0 1-.658.643 49.118 49.118 0 0 1-4.708-.36.75.75 0 0 1-.645-.878c.293-1.614.504-3.257.629-4.924A.53.53 0 0 0 5.337 15c-.355 0-.676.186-.959.401-.29.221-.634.349-1.003.349-1.036 0-1.875-1.007-1.875-2.25s.84-2.25 1.875-2.25c.369 0 .713.128 1.003.349.283.215.604.401.959.401a.656.656 0 0 0 .659-.663 47.703 47.703 0 0 0-.31-4.82.75.75 0 0 1 .83-.832c1.343.155 2.703.254 4.077.294a.64.64 0 0 0 .657-.642Z" /></svg>'
|
return '<svg class="group-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#fff" width="1.6em" height="1.6em"><path d="M11.25 5.337c0-.355-.186-.676-.401-.959a1.647 1.647 0 0 1-.349-1.003c0-1.036 1.007-1.875 2.25-1.875S15 2.34 15 3.375c0 .369-.128.713-.349 1.003-.215.283-.401.604-.401.959 0 .332.278.598.61.578 1.91-.114 3.79-.342 5.632-.676a.75.75 0 0 1 .878.645 49.17 49.17 0 0 1 .376 5.452.657.657 0 0 1-.66.664c-.354 0-.675-.186-.958-.401a1.647 1.647 0 0 0-1.003-.349c-1.035 0-1.875 1.007-1.875 2.25s.84 2.25 1.875 2.25c.369 0 .713-.128 1.003-.349.283-.215.604-.401.959-.401.31 0 .557.262.534.571a48.774 48.774 0 0 1-.595 4.845.75.75 0 0 1-.61.61c-1.82.317-3.673.533-5.555.642a.58.58 0 0 1-.611-.581c0-.355.186-.676.401-.959.221-.29.349-.634.349-1.003 0-1.035-1.007-1.875-2.25-1.875s-2.25.84-2.25 1.875c0 .369.128.713.349 1.003.215.283.401.604.401.959a.641.641 0 0 1-.658.643 49.118 49.118 0 0 1-4.708-.36.75.75 0 0 1-.645-.878c.293-1.614.504-3.257.629-4.924A.53.53 0 0 0 5.337 15c-.355 0-.676.186-.959.401-.29.221-.634.349-1.003.349-1.036 0-1.875-1.007-1.875-2.25s.84-2.25 1.875-2.25c.369 0 .713.128 1.003.349.283.215.604.401.959.401a.656.656 0 0 0 .659-.663 47.703 47.703 0 0 0-.31-4.82.75.75 0 0 1 .83-.832c1.343.155 2.703.254 4.077.294a.64.64 0 0 0 .657-.642Z" /></svg>'
|
||||||
case 'staff-snake':
|
case 'staff-snake':
|
||||||
return '<svg class="staff-snake-icon" stroke="currentColor" fill="#fff" stroke-width="0" viewBox="0 0 384 512" height="1.4em" width="1.4em" xmlns="http://www.w3.org/2000/svg"><path d="M222.6 43.2l-.1 4.8H288c53 0 96 43 96 96s-43 96-96 96H248V160h40c8.8 0 16-7.2 16-16s-7.2-16-16-16H248 220l-4.5 144H256c53 0 96 43 96 96s-43 96-96 96H240V384h16c8.8 0 16-7.2 16-16s-7.2-16-16-16H213l-3.1 99.5L208.5 495l0 1c-.3 8.9-7.6 16-16.5 16s-16.2-7.1-16.5-16l0-1-1-31H136c-22.1 0-40-17.9-40-40s17.9-40 40-40h36l-1-32H152c-53 0-96-43-96-96c0-47.6 34.6-87.1 80-94.7V256c0 8.8 7.2 16 16 16h16.5L164 128H136 122.6c-9 18.9-28.3 32-50.6 32H56c-30.9 0-56-25.1-56-56S25.1 48 56 48h8 8 89.5l-.1-4.8L161 32c0-.7 0-1.3 0-1.9c.5-16.6 14.1-30 31-30s30.5 13.4 31 30c0 .6 0 1.3 0 1.9l-.4 11.2zM64 112a16 16 0 1 0 0-32 16 16 0 1 0 0 32z"></path></svg>'
|
return '<svg class="staff-snake-icon" stroke="currentColor" fill="#fff" stroke-width="0" viewBox="0 0 384 512" height="1.4em" width="1.4em" xmlns="http://www.w3.org/2000/svg"><path d="M222.6 43.2l-.1 4.8H288c53 0 96 43 96 96s-43 96-96 96H248V160h40c8.8 0 16-7.2 16-16s-7.2-16-16-16H248 220l-4.5 144H256c53 0 96 43 96 96s-43 96-96 96H240V384h16c8.8 0 16-7.2 16-16s-7.2-16-16-16H213l-3.1 99.5L208.5 495l0 1c-.3 8.9-7.6 16-16.5 16s-16.2-7.1-16.5-16l0-1-1-31H136c-22.1 0-40-17.9-40-40s17.9-40 40-40h36l-1-32H152c-53 0-96-43-96-96c0-47.6 34.6-87.1 80-94.7V256c0 8.8 7.2 16 16 16h16.5L164 128H136 122.6c-9 18.9-28.3 32-50.6 32H56c-30.9 0-56-25.1-56-56S25.1 48 56 48h8 8 89.5l-.1-4.8L161 32c0-.7 0-1.3 0-1.9c.5-16.6 14.1-30 31-30s30.5 13.4 31 30c0 .6 0 1.3 0 1.9l-.4 11.2zM64 112a16 16 0 1 0 0-32 16 16 0 1 0 0 32z"></path></svg>'
|
||||||
|
case 'flower':
|
||||||
|
return '<svg class="flower-icon" stroke="currentColor" fill="#fff" stroke-width="0" viewBox="0 0 256 256" height="1.5em" width="1.5em" xmlns="http://www.w3.org/2000/svg"><path d="M210.35,129.36c-.81-.47-1.7-.92-2.62-1.36.92-.44,1.81-.89,2.62-1.36a40,40,0,1,0-40-69.28c-.81.47-1.65,1-2.48,1.59.08-1,.13-2,.13-3a40,40,0,0,0-80,0c0,.94,0,1.94.13,3-.83-.57-1.67-1.12-2.48-1.59a40,40,0,1,0-40,69.28c.81.47,1.7.92,2.62,1.36-.92.44-1.81.89-2.62,1.36a40,40,0,1,0,40,69.28c.81-.47,1.65-1,2.48-1.59-.08,1-.13,2-.13,2.95a40,40,0,0,0,80,0c0-.94-.05-1.94-.13-2.95.83.57,1.67,1.12,2.48,1.59A39.79,39.79,0,0,0,190.29,204a40.43,40.43,0,0,0,10.42-1.38,40,40,0,0,0,9.64-73.28ZM104,128a24,24,0,1,1,24,24A24,24,0,0,1,104,128Zm74.35-56.79a24,24,0,1,1,24,41.57c-6.27,3.63-18.61,6.13-35.16,7.19A40,40,0,0,0,154.53,98.1C163.73,84.28,172.08,74.84,178.35,71.21ZM128,32a24,24,0,0,1,24,24c0,7.24-4,19.19-11.36,34.06a39.81,39.81,0,0,0-25.28,0C108,75.19,104,63.24,104,56A24,24,0,0,1,128,32ZM44.86,80a24,24,0,0,1,32.79-8.79c6.27,3.63,14.62,13.07,23.82,26.89A40,40,0,0,0,88.81,120c-16.55-1.06-28.89-3.56-35.16-7.18A24,24,0,0,1,44.86,80ZM77.65,184.79a24,24,0,1,1-24-41.57c6.27-3.63,18.61-6.13,35.16-7.19a40,40,0,0,0,12.66,21.87C92.27,171.72,83.92,181.16,77.65,184.79ZM128,224a24,24,0,0,1-24-24c0-7.24,4-19.19,11.36-34.06a39.81,39.81,0,0,0,25.28,0C148,180.81,152,192.76,152,200A24,24,0,0,1,128,224Zm83.14-48a24,24,0,0,1-32.79,8.79c-6.27-3.63-14.62-13.07-23.82-26.89A40,40,0,0,0,167.19,136c16.55,1.06,28.89,3.56,35.16,7.18A24,24,0,0,1,211.14,176Z"></path></svg>'
|
||||||
|
case 'network':
|
||||||
|
return '<svg class="network-icon" stroke="currentColor" fill="#fff" stroke-width="0" viewBox="0 0 256 256" height="1.5em" width="1.5em" xmlns="http://www.w3.org/2000/svg"><path d="M212,200a36,36,0,1,1-69.85-12.25l-53-34.05a36,36,0,1,1,0-51.4l53-34a36.09,36.09,0,1,1,8.67,13.45l-53,34.05a36,36,0,0,1,0,24.5l53,34.05A36,36,0,0,1,212,200Z"></path></svg>'
|
||||||
default:
|
default:
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user