mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
cleanup profile form
This commit is contained in:
parent
fd989e9b16
commit
86b442aabf
@ -1,106 +1,53 @@
|
||||
import { useItems, useUpdateItem, useAddItem } from '../Map/hooks/useItems'
|
||||
import { useEffect, useState } from 'react';
|
||||
import { getValue } from '../../Utils/GetValue';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useAuth } from '../Auth';
|
||||
import { TextInput, TextAreaInput } from '../Input';
|
||||
import ComboBoxInput from '../Input/ComboBoxInput';
|
||||
import { ColorPicker } from './Subcomponents/ColorPicker';
|
||||
import { hashTagRegex } from '../../Utils/HashTagRegex';
|
||||
import { useAddTag, useGetItemTags, useTags } from '../Map/hooks/useTags';
|
||||
import { randomColor } from '../../Utils/RandomColor';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { Item, Tag } from '../../types';
|
||||
import { MapOverlayPage } from '../Templates';
|
||||
import { AvatarWidget } from './Subcomponents/AvatarWidget';
|
||||
import { encodeTag } from '../../Utils/FormatTags';
|
||||
import { useLayers } from '../Map/hooks/useLayers';
|
||||
import { TagsWidget } from './Subcomponents/TagsWidget';
|
||||
import { LinkedItemsHeaderView } from './Subcomponents/LinkedItemsHeaderView';
|
||||
import { TextView } from '../Map';
|
||||
import { ActionButton } from './Subcomponents/ActionsButton';
|
||||
import { useHasUserPermission } from '../Map/hooks/usePermissions';
|
||||
|
||||
|
||||
import { OnepagerForm } from './Templates/OnepagerForm';
|
||||
import { linkItem, onUpdateItem, unlinkItem } from './itemFunctions';
|
||||
import { SimpleForm } from './Templates/SimpleForm';
|
||||
import { TabsForm } from './Templates/TabsForm';
|
||||
import { FormHeader } from './Subcomponents/FormHeader';
|
||||
|
||||
export function ProfileForm({ userType }: { userType: string }) {
|
||||
|
||||
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' }
|
||||
];
|
||||
|
||||
const [id, setId] = useState<string>("");
|
||||
const [groupType, setGroupType] = useState<string>("");
|
||||
const [status, setStatus] = useState<string>("");
|
||||
const [name, setName] = useState<string>("");
|
||||
const [subname, setSubname] = useState<string>("");
|
||||
const [text, setText] = useState<string>("");
|
||||
const [contact, setContact] = useState<string>("");
|
||||
const [telephone, setTelephone] = useState<string>("");
|
||||
const [nextAppointment, setNextAppointment] = useState<string>("");
|
||||
const [markerIcon, setMarkerIcon] = useState<string>("");
|
||||
const [image, setImage] = useState<string>("");
|
||||
const [color, setColor] = useState<string>("");
|
||||
const [offers, setOffers] = useState<Array<Tag>>([]);
|
||||
const [needs, setNeeds] = useState<Array<Tag>>([]);
|
||||
const [relations, setRelations] = useState<Array<Item>>([]);
|
||||
const [state, setState] = useState({
|
||||
color: "",
|
||||
id: "",
|
||||
groupType: "wuerdekompass",
|
||||
status: "active",
|
||||
name: "",
|
||||
subname: "",
|
||||
text: "",
|
||||
contact: "",
|
||||
telephone: "",
|
||||
nextAppointment: "",
|
||||
image: "",
|
||||
markerIcon: "",
|
||||
offers: [] as Tag[],
|
||||
needs: [] as Tag[],
|
||||
relations: [] as Item[]
|
||||
});
|
||||
|
||||
const [updatePermission, setUpdatePermission] = useState<boolean>(false);
|
||||
|
||||
|
||||
const [activeTab, setActiveTab] = useState<number>(1);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [item, setItem] = useState<Item>({} as Item)
|
||||
const { user } = useAuth();
|
||||
|
||||
const updateItem = useUpdateItem();
|
||||
const addItem = useAddItem();
|
||||
const layers = useLayers();
|
||||
const location = useLocation();
|
||||
|
||||
|
||||
const tags = useTags();
|
||||
const addTag = useAddTag();
|
||||
const navigate = useNavigate();
|
||||
const hasUserPermission = useHasUserPermission();
|
||||
const getItemTags = useGetItemTags();
|
||||
|
||||
useEffect(() => {
|
||||
switch (groupType) {
|
||||
case "wuerdekompass":
|
||||
setColor(item?.layer?.menuColor || "#1A5FB4");
|
||||
setMarkerIcon("group");
|
||||
setImage("59e6a346-d1ee-4767-9e42-fc720fb535c9")
|
||||
|
||||
break;
|
||||
case "themenkompass":
|
||||
setColor("#26A269");
|
||||
setMarkerIcon("group");
|
||||
setImage("59e6a346-d1ee-4767-9e42-fc720fb535c9")
|
||||
|
||||
break;
|
||||
case "liebevoll.jetzt":
|
||||
setColor("#E8B620");
|
||||
setMarkerIcon("liebevoll.jetzt");
|
||||
setImage("e735b96c-507b-471c-8317-386ece0ca51d")
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}, [groupType])
|
||||
|
||||
|
||||
|
||||
|
||||
const items = useItems();
|
||||
const [item, setItem] = useState<Item>({} as Item)
|
||||
|
||||
useEffect(() => {
|
||||
item && hasUserPermission("items", "update", item) && setUpdatePermission(true);
|
||||
@ -118,201 +65,49 @@ export function ProfileForm({ userType }: { userType: string }) {
|
||||
|
||||
}, [items])
|
||||
|
||||
const updateActiveTab = (id: number) => {
|
||||
setActiveTab(id);
|
||||
|
||||
let params = new URLSearchParams(window.location.search);
|
||||
let urlTab = params.get("tab");
|
||||
if (!urlTab?.includes(id.toString()))
|
||||
params.set("tab", `${id ? id : ""}`)
|
||||
window.history.pushState('', '', "?" + params.toString());
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
let params = new URLSearchParams(location.search);
|
||||
let urlTab = params.get("tab");
|
||||
urlTab ? setActiveTab(Number(urlTab)) : setActiveTab(1);
|
||||
}, [location])
|
||||
const newColor = item.layer?.itemColorField && getValue(item, item.layer?.itemColorField)
|
||||
? getValue(item, item.layer?.itemColorField)
|
||||
: (getItemTags(item) && getItemTags(item)[0]?.color)
|
||||
? getItemTags(item)[0].color
|
||||
: item?.layer?.markerDefaultColor;
|
||||
|
||||
const offers = (item?.offers ?? []).reduce((acc: Tag[], o) => {
|
||||
const offer = tags.find(t => t.id === o.tags_id);
|
||||
if (offer) acc.push(offer);
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const needs = (item?.needs ?? []).reduce((acc: Tag[], o) => {
|
||||
const need = tags.find(t => t.id === o.tags_id);
|
||||
if (need) acc.push(need);
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setColor(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))
|
||||
const relations = (item?.relations ?? []).reduce((acc: Item[], r) => {
|
||||
const relatedItem = items.find(i => i.id === r.related_items_id);
|
||||
if (relatedItem) acc.push(relatedItem);
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
setId(item?.id ? item.id : "");
|
||||
setGroupType(item?.group_type || "wuerdekompass");
|
||||
setStatus(item?.status || "active");
|
||||
setName(item?.name ? item.name : "");
|
||||
setSubname(item?.subname ? item.subname : "");
|
||||
setText(item?.text ? item.text : "");
|
||||
setContact(item?.contact || "");
|
||||
setTelephone(item?.telephone || "");
|
||||
setNextAppointment(item?.next_appointment || "");
|
||||
setImage(item?.image ? item?.image : "");
|
||||
setMarkerIcon(item?.marker_icon ? item.marker_icon : "");
|
||||
setOffers([]);
|
||||
setNeeds([]);
|
||||
setRelations([]);
|
||||
item?.offers?.map(o => {
|
||||
const offer = tags?.find(t => t.id === o.tags_id);
|
||||
offer && setOffers(current => [...current, offer])
|
||||
})
|
||||
item?.needs?.map(o => {
|
||||
const need = tags?.find(t => t.id === o.tags_id);
|
||||
need && setNeeds(current => [...current, need])
|
||||
})
|
||||
item.relations?.map(r => {
|
||||
const item = items.find(i => i.id == r.related_items_id)
|
||||
item && setRelations(current => [...current, item])
|
||||
})
|
||||
|
||||
}, [item])
|
||||
|
||||
|
||||
const onUpdateItem = async () => {
|
||||
let changedItem = {} as Item;
|
||||
|
||||
let offer_updates: Array<any> = [];
|
||||
//check for new offers
|
||||
offers?.map(o => {
|
||||
const existingOffer = item?.offers?.find(t => t.tags_id === o.id)
|
||||
existingOffer && offer_updates.push(existingOffer.id)
|
||||
if (!existingOffer && !tags.some(t => t.id === o.id)) addTag({ ...o, offer_or_need: true })
|
||||
!existingOffer && offer_updates.push({ items_id: item?.id, tags_id: o.id })
|
||||
setState({
|
||||
color: newColor,
|
||||
id: item?.id ?? "",
|
||||
groupType: item?.group_type ?? "wuerdekompass",
|
||||
status: item?.status ?? "active",
|
||||
name: item?.name ?? "",
|
||||
subname: item?.subname ?? "",
|
||||
text: item?.text ?? "",
|
||||
contact: item?.contact ?? "",
|
||||
telephone: item?.telephone ?? "",
|
||||
nextAppointment: item?.next_appointment ?? "",
|
||||
image: item?.image ?? "",
|
||||
markerIcon: item?.marker_icon ?? "",
|
||||
offers: offers,
|
||||
needs: needs,
|
||||
relations: relations
|
||||
});
|
||||
|
||||
let needs_updates: Array<any> = [];
|
||||
|
||||
needs?.map(n => {
|
||||
const existingNeed = item?.needs?.find(t => t.tags_id === n.id)
|
||||
existingNeed && needs_updates.push(existingNeed.id)
|
||||
!existingNeed && needs_updates.push({ items_id: item?.id, tags_id: n.id })
|
||||
!existingNeed && !tags.some(t => t.id === n.id) && addTag({ ...n, offer_or_need: true })
|
||||
});
|
||||
|
||||
|
||||
// update profile item in current state
|
||||
changedItem = {
|
||||
id: id,
|
||||
group_type: groupType,
|
||||
status: status,
|
||||
name: name,
|
||||
subname: subname,
|
||||
text: text,
|
||||
color: color,
|
||||
position: item.position,
|
||||
contact: contact,
|
||||
telephone: telephone,
|
||||
...markerIcon && { markerIcon: markerIcon },
|
||||
next_appointment: nextAppointment,
|
||||
...image.length > 10 && { image: image },
|
||||
...offers.length > 0 && { offers: offer_updates },
|
||||
...needs.length > 0 && { needs: needs_updates }
|
||||
};
|
||||
|
||||
let offers_state: Array<any> = [];
|
||||
let needs_state: Array<any> = [];
|
||||
|
||||
await offers.map(o => {
|
||||
offers_state.push({ items_id: item?.id, tags_id: o.id })
|
||||
});
|
||||
|
||||
await needs.map(n => {
|
||||
needs_state.push({ items_id: item?.id, tags_id: n.id })
|
||||
});
|
||||
|
||||
changedItem = { ...changedItem, offers: offers_state, needs: needs_state };
|
||||
|
||||
|
||||
text.toLocaleLowerCase().match(hashTagRegex)?.map(tag => {
|
||||
if (!tags.find((t) => t.name.toLocaleLowerCase() === tag.slice(1).toLocaleLowerCase())) {
|
||||
addTag({ id: crypto.randomUUID(), name: encodeTag(tag.slice(1).toLocaleLowerCase()), color: randomColor() })
|
||||
}
|
||||
});
|
||||
|
||||
setLoading(true);
|
||||
console.log(item.layer);
|
||||
|
||||
|
||||
if (!item.new) {
|
||||
item?.layer?.api?.updateItem && toast.promise(
|
||||
item?.layer?.api?.updateItem(changedItem),
|
||||
{
|
||||
pending: 'updating Item ...',
|
||||
success: 'Item updated',
|
||||
error: {
|
||||
render({ data }) {
|
||||
return `${data}`
|
||||
},
|
||||
},
|
||||
})
|
||||
.then(() => item && updateItem({ ...item, ...changedItem }))
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
navigate(`/item/${item.id}${params && "?"+params}`)
|
||||
});
|
||||
|
||||
}
|
||||
else {
|
||||
item.layer?.api?.createItem && toast.promise(
|
||||
item.layer?.api?.createItem(changedItem),
|
||||
{
|
||||
pending: 'updating Item ...',
|
||||
success: 'Item updated',
|
||||
error: {
|
||||
render({ data }) {
|
||||
return `${data}`
|
||||
},
|
||||
},
|
||||
})
|
||||
.then(() => item && addItem({ ...item, ...changedItem, layer: item.layer, user_created: user, type: item.layer?.itemType }))
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
navigate(`/${params && "?"+params}`)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const linkItem = async (id: string) => {
|
||||
let new_relations = item.relations || [];
|
||||
new_relations?.push({ items_id: item.id, related_items_id: id })
|
||||
const updatedItem = { id: item.id, relations: new_relations }
|
||||
|
||||
let success = false;
|
||||
try {
|
||||
await item?.layer?.api?.updateItem!(updatedItem)
|
||||
success = true;
|
||||
} catch (error) {
|
||||
toast.error(error.toString());
|
||||
}
|
||||
if (success) {
|
||||
updateItem({ ...item, relations: new_relations })
|
||||
toast.success("Item linked");
|
||||
}
|
||||
}
|
||||
|
||||
const unlinkItem = async (id: string) => {
|
||||
console.log(id);
|
||||
|
||||
let new_relations = item.relations?.filter(r => r.related_items_id !== id)
|
||||
console.log(new_relations);
|
||||
|
||||
const updatedItem = { id: item.id, relations: new_relations }
|
||||
|
||||
|
||||
let success = false;
|
||||
try {
|
||||
await item?.layer?.api?.updateItem!(updatedItem)
|
||||
success = true;
|
||||
} catch (error) {
|
||||
toast.error(error.toString());
|
||||
}
|
||||
if (success) {
|
||||
updateItem({ ...item, relations: new_relations })
|
||||
toast.success("Item unlinked");
|
||||
}
|
||||
|
||||
}
|
||||
}, [item, tags, items]);
|
||||
|
||||
const [template, setTemplate] = useState<string>("")
|
||||
|
||||
@ -322,161 +117,33 @@ export function ProfileForm({ userType }: { userType: string }) {
|
||||
|
||||
let params = new URLSearchParams(window.location.search);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<MapOverlayPage 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'>
|
||||
|
||||
<div className='tw-flex tw-flex-col tw-h-full'>
|
||||
<div className="tw-flex">
|
||||
<AvatarWidget avatar={image} setAvatar={setImage} />
|
||||
<ColorPicker color={color} onChange={setColor} 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) => setName(v)} containerStyle='tw-grow tw-input-md' />
|
||||
<TextInput placeholder="Subtitle" defaultValue={item?.subname ? item.subname : ""} updateFormValue={(v) => setSubname(v)} containerStyle='tw-grow tw-input-sm tw-px-4 tw-mt-1' />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FormHeader item={item} state={state} setState={setState} />
|
||||
|
||||
{template == "onepager" && (
|
||||
<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">
|
||||
<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={typeMapping}
|
||||
value={groupType}
|
||||
onValueChange={(v) => setGroupType(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={status}
|
||||
onValueChange={(v) => setStatus(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={contact}
|
||||
updateFormValue={(v) => setContact(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={telephone}
|
||||
updateFormValue={(v) => setTelephone(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={nextAppointment}
|
||||
updateFormValue={(v) => setNextAppointment(v)}
|
||||
inputStyle="tw-h-24"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="description" className="tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1">
|
||||
Gruppenbeschreibung:
|
||||
</label>
|
||||
<TextAreaInput
|
||||
placeholder="Beschreibung"
|
||||
defaultValue={item?.text ?? ""}
|
||||
updateFormValue={(v) => setText(v)}
|
||||
inputStyle="tw-h-48"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<OnepagerForm item={item} state={state} setState={setState}></OnepagerForm>
|
||||
)}
|
||||
|
||||
{template == "simple" &&
|
||||
<TextAreaInput placeholder="About me ..." defaultValue={item?.text ? item.text : ""} updateFormValue={(v) => { console.log(v); setText(v) }} containerStyle='tw-mt-8 tw-h-full' inputStyle='tw-h-full' />
|
||||
|
||||
<SimpleForm item={item} setState={setState}></SimpleForm>
|
||||
}
|
||||
|
||||
{template == "tabs" &&
|
||||
|
||||
|
||||
<div role="tablist" className="tw-tabs tw-tabs-lifted tw-mt-4">
|
||||
<input type="radio" name="my_tabs_2" role="tab" className={`tw-tab [--tab-border-color:var(--fallback-bc,oklch(var(--bc)/0.2))]`} aria-label="Info" 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">
|
||||
<TextAreaInput placeholder="About me ..." defaultValue={item?.text ? item.text : ""} updateFormValue={(v) => { console.log(v); setText(v) }} containerStyle='tw-h-full' inputStyle='tw-h-full tw-border-t-0 tw-rounded-tl-none' />
|
||||
</div>
|
||||
{item.layer?.itemType.offers_and_needs &&
|
||||
<>
|
||||
<input type="radio" name="my_tabs_2" role="tab" className={`tw-tab tw-min-w-[10em] [--tab-border-color:var(--fallback-bc,oklch(var(--bc)/0.2))]`} aria-label="Offers & Needs" checked={activeTab == 3 && true} onChange={() => updateActiveTab(3)} />
|
||||
<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 className='tw-h-full'>
|
||||
<div className='tw-w-full tw-h-[calc(50%-0.75em)] tw-mb-4'>
|
||||
<TagsWidget defaultTags={offers} onUpdate={(v) => setOffers(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' />
|
||||
</div>
|
||||
<div className='tw-w-full tw-h-[calc(50%-0.75em)] '>
|
||||
<TagsWidget defaultTags={needs} onUpdate={(v) => setNeeds(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' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
{item.layer?.itemType.relations &&
|
||||
<>
|
||||
<input type="radio" name="my_tabs_2" role="tab" className="tw-tab [--tab-border-color:var(--fallback-bc,oklch(var(--bc)/0.2))]" aria-label="Relations" checked={activeTab == 7 && true} onChange={() => updateActiveTab(7)} />
|
||||
<div role="tabpanel" 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">
|
||||
<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'>
|
||||
{relations && relations.map(i =>
|
||||
|
||||
|
||||
<div 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' onClick={() => navigate('/item/' + i.id)}>
|
||||
<LinkedItemsHeaderView unlinkPermission={updatePermission} item={i} unlinkCallback={unlinkItem} loading={loading} />
|
||||
<div className='tw-overflow-y-auto tw-overflow-x-hidden tw-max-h-64 fade'>
|
||||
<TextView truncate item={i} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{updatePermission && <ActionButton customStyle="!tw-bottom-20" collection="items" item={item} existingRelations={relations} triggerItemSelected={linkItem} colorField={item.layer.itemColorField}></ActionButton>}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
|
||||
</div>
|
||||
<TabsForm loading={loading} item={item} state={state} setState={setState} updatePermission={updatePermission} linkItem={(id) => linkItem(id, item, updateItem)} unlinkItem={(id) => unlinkItem(id, item, updateItem)}></TabsForm>
|
||||
}
|
||||
|
||||
|
||||
|
||||
<div className="tw-mt-4 tw-mb-4"><button className={loading ? " tw-loading tw-btn tw-float-right" : "tw-btn tw-float-right"} onClick={() => onUpdateItem()} 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)}`, color: "#fff" } : { color: "#fff" }}>Update</button></div>
|
||||
<div className="tw-mt-4 tw-mb-4">
|
||||
<button className={loading ? " tw-loading tw-btn tw-float-right" : "tw-btn tw-float-right"} onClick={() => onUpdateItem(state, item, tags, addTag, setLoading, navigate, updateItem, addItem, user, params)} 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)}`, color: "#fff" } : { color: "#fff" }}>Update</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</MapOverlayPage>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import { MapOverlayPage } from '../Templates'
|
||||
import { useItems, useRemoveItem, useUpdateItem } from '../Map/hooks/useItems'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Item, Tag } from '../../types';
|
||||
import { useMap } from 'react-leaflet';
|
||||
import { LatLng } from 'leaflet';
|
||||
@ -11,26 +11,25 @@ import { useSelectPosition, useSetSelectPosition } from '../Map/hooks/useSelectP
|
||||
import { useClusterRef } from '../Map/hooks/useClusterRef';
|
||||
import { useLeafletRefs } from '../Map/hooks/useLeafletRefs';
|
||||
import { getValue } from '../../Utils/GetValue';
|
||||
import { Tabs } from './Templates/Tabs';
|
||||
import { Onepager } from './Templates/Onepager';
|
||||
import { Simple } from './Templates/Simple';
|
||||
import { TabsView } from './Templates/TabsView';
|
||||
import { OnepagerView } from './Templates/OnepagerView';
|
||||
import { SimpleView } from './Templates/SimpleView';
|
||||
import { handleDelete, linkItem, unlinkItem } from './itemFunctions';
|
||||
import { useTags } from '../Map/hooks/useTags';
|
||||
|
||||
export function ProfileView({ userType }: { userType: string }) {
|
||||
|
||||
const [item, setItem] = useState<Item>({} as Item)
|
||||
const [updatePermission, setUpdatePermission] = useState<boolean>(false);
|
||||
const [relations, setRelations] = useState<Array<Item>>([]);
|
||||
const [offers, setOffers] = useState<Array<Tag>>([]);
|
||||
const [needs, setNeeds] = useState<Array<Tag>>([]);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
|
||||
const [addItemPopupType, setAddItemPopupType] = useState<string>("");
|
||||
const [template, setTemplate] = useState<string>("");
|
||||
|
||||
const location = useLocation();
|
||||
const items = useItems();
|
||||
const updateItem = useUpdateItem();
|
||||
const [item, setItem] = useState<Item>({} as Item)
|
||||
const map = useMap();
|
||||
const selectPosition = useSelectPosition();
|
||||
const removeItem = useRemoveItem();
|
||||
@ -41,16 +40,6 @@ export function ProfileView({ userType }: { userType: string }) {
|
||||
const clusterRef = useClusterRef();
|
||||
const leafletRefs = useLeafletRefs();
|
||||
|
||||
const tabRef = useRef<HTMLFormElement>(null);
|
||||
|
||||
function scroll() {
|
||||
tabRef.current?.scrollIntoView();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
scroll();
|
||||
}, [addItemPopupType])
|
||||
|
||||
useEffect(() => {
|
||||
const itemId = location.pathname.split("/")[2];
|
||||
const item = items.find(i => i.id === itemId);
|
||||
@ -126,9 +115,6 @@ export function ProfileView({ userType }: { userType: string }) {
|
||||
selectPosition && map.closePopup();
|
||||
}, [selectPosition])
|
||||
|
||||
|
||||
const [template, setTemplate] = useState<string>("")
|
||||
|
||||
useEffect(() => {
|
||||
setTemplate(item.layer?.itemType.template || userType);
|
||||
}, [userType, item])
|
||||
@ -142,17 +128,16 @@ export function ProfileView({ userType }: { userType: string }) {
|
||||
<HeaderView api={item.layer?.api} item={item} deleteCallback={(e) => handleDelete(e, item, setLoading, removeItem, map, navigate)} editCallback={() => navigate("/edit-item/" + item.id)} setPositionCallback={() => { map.closePopup(); setSelectPosition(item); navigate("/") }} big truncateSubname={false} />
|
||||
</div>
|
||||
|
||||
|
||||
{template == "onepager" &&
|
||||
<Onepager item={item} userType={userType}/>
|
||||
<OnepagerView item={item} userType={userType}/>
|
||||
}
|
||||
|
||||
{template == "simple" &&
|
||||
<Simple item={item}></Simple>
|
||||
<SimpleView item={item}/>
|
||||
}
|
||||
|
||||
{template == "tabs" &&
|
||||
<Tabs item={item} loading={loading} offers={offers} needs={needs} relations={relations} updatePermission={updatePermission} linkItem={(id) => linkItem(id, item, updateItem)} unlinkItem={(id) => unlinkItem(id, item, updateItem)}/>
|
||||
<TabsView item={item} loading={loading} offers={offers} needs={needs} relations={relations} updatePermission={updatePermission} linkItem={(id) => linkItem(id, item, updateItem)} unlinkItem={(id) => unlinkItem(id, item, updateItem)}/>
|
||||
}
|
||||
</>
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ import DialogModal from "../../Templates/DialogModal";
|
||||
import 'react-image-crop/dist/ReactCrop.css'
|
||||
|
||||
|
||||
export const AvatarWidget = ({avatar, setAvatar}:{avatar:string, setAvatar : React.Dispatch<React.SetStateAction<string>>}) => {
|
||||
export const AvatarWidget = ({avatar, setAvatar}:{avatar:string, setAvatar : React.Dispatch<React.SetStateAction<any>>}) => {
|
||||
|
||||
|
||||
const [crop, setCrop] = useState<Crop>();
|
||||
|
||||
28
src/Components/Profile/Subcomponents/FormHeader.tsx
Normal file
28
src/Components/Profile/Subcomponents/FormHeader.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import { TextInput } from "utopia-ui"
|
||||
import { AvatarWidget } from "./AvatarWidget"
|
||||
import { ColorPicker } from "./ColorPicker"
|
||||
|
||||
export const FormHeader = ({item, state, setState}) => {
|
||||
return (
|
||||
<div className="tw-flex">
|
||||
<AvatarWidget avatar={state.image} setAvatar={(i) => setState(prevState => ({
|
||||
...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 => ({
|
||||
...prevState,
|
||||
name: v
|
||||
}))} containerStyle='tw-grow tw-input-md' />
|
||||
<TextInput placeholder="Subtitle" defaultValue={item?.subname ? item.subname : ""} updateFormValue={(v) => setState(prevState => ({
|
||||
...prevState,
|
||||
subname: v
|
||||
}))} containerStyle='tw-grow tw-input-sm tw-px-4 tw-mt-1' />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
164
src/Components/Profile/Templates/OnepagerForm.tsx
Normal file
164
src/Components/Profile/Templates/OnepagerForm.tsx
Normal file
@ -0,0 +1,164 @@
|
||||
import { useEffect } from "react";
|
||||
import { Item, Tag } from "../../../types"
|
||||
import { TextAreaInput, TextInput } from "../../Input"
|
||||
import ComboBoxInput from "../../Input/ComboBoxInput"
|
||||
|
||||
export const OnepagerForm = ({ item, state, setState }: {
|
||||
state: {
|
||||
color: string;
|
||||
id: string;
|
||||
groupType: string;
|
||||
status: string;
|
||||
name: string;
|
||||
subname: string;
|
||||
text: string;
|
||||
contact: string;
|
||||
telephone: string;
|
||||
nextAppointment: string;
|
||||
image: string;
|
||||
markerIcon: string;
|
||||
offers: Tag[];
|
||||
needs: Tag[];
|
||||
relations: Item[];
|
||||
},
|
||||
setState: React.Dispatch<React.SetStateAction<any>>,
|
||||
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;
|
||||
}
|
||||
}, [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 (
|
||||
<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">
|
||||
<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={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>
|
||||
<label htmlFor="description" className="tw-block tw-text-sm tw-font-medium tw-text-gray-500 tw-mb-1">
|
||||
Gruppenbeschreibung:
|
||||
</label>
|
||||
<TextAreaInput
|
||||
placeholder="Beschreibung"
|
||||
defaultValue={state.text || ""}
|
||||
updateFormValue={(v) => setState(prevState => ({
|
||||
...prevState,
|
||||
text: v
|
||||
}))}
|
||||
inputStyle="tw-h-48"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -5,7 +5,7 @@ import ProfileSubHeader from "../Subcomponents/ProfileSubHeader"
|
||||
import { useEffect, useState } from "react"
|
||||
import { useItems } from "../../Map/hooks/useItems"
|
||||
|
||||
export const Onepager = ({item, userType}:{item: Item, userType: string}) => {
|
||||
export const OnepagerView = ({item, userType}:{item: Item, userType: string}) => {
|
||||
|
||||
const [profile_owner, setProfileOwner] = useState<Item>();
|
||||
const items = useItems();
|
||||
10
src/Components/Profile/Templates/SimpleForm.tsx
Normal file
10
src/Components/Profile/Templates/SimpleForm.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import { TextAreaInput } from "../../Input"
|
||||
|
||||
export const SimpleForm = (item, setState) => {
|
||||
return (
|
||||
<TextAreaInput placeholder="About me ..." defaultValue={item?.text ? item.text : ""} updateFormValue={(v) => setState(prevState => ({
|
||||
...prevState,
|
||||
text: v
|
||||
}))} containerStyle='tw-mt-8 tw-h-full' inputStyle='tw-h-full' />
|
||||
)
|
||||
}
|
||||
@ -2,7 +2,7 @@ import * as React from 'react'
|
||||
import { TextView } from '../../Map'
|
||||
import { Item } from '../../../types'
|
||||
|
||||
export const Simple = ({item}:{item: Item}) => {
|
||||
export const SimpleView = ({item}:{item: Item}) => {
|
||||
return (
|
||||
<div className='tw-mt-8 tw-h-full tw-overflow-y-auto fade tw-px-6'>
|
||||
<TextView item={item} />
|
||||
89
src/Components/Profile/Templates/TabsForm.tsx
Normal file
89
src/Components/Profile/Templates/TabsForm.tsx
Normal file
@ -0,0 +1,89 @@
|
||||
import { useEffect, useState } from "react"
|
||||
import { TextAreaInput } from "../../Input"
|
||||
import { TextView } from "../../Map"
|
||||
import { ActionButton } from "../Subcomponents/ActionsButton"
|
||||
import { LinkedItemsHeaderView } from "../Subcomponents/LinkedItemsHeaderView"
|
||||
import { TagsWidget } from "../Subcomponents/TagsWidget"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useUpdateItem } from "../../Map/hooks/useItems"
|
||||
|
||||
export const TabsForm = ({item, state, setState, updatePermission, linkItem, unlinkItem, loading}) => {
|
||||
|
||||
const [activeTab, setActiveTab] = useState<number>(1);
|
||||
const navigate = useNavigate();
|
||||
const updateItem = useUpdateItem();
|
||||
|
||||
const updateActiveTab = (id: number) => {
|
||||
setActiveTab(id);
|
||||
|
||||
let params = new URLSearchParams(window.location.search);
|
||||
let urlTab = params.get("tab");
|
||||
if (!urlTab?.includes(id.toString()))
|
||||
params.set("tab", `${id ? id : ""}`)
|
||||
window.history.pushState('', '', "?" + params.toString());
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
let params = new URLSearchParams(location.search);
|
||||
let urlTab = params.get("tab");
|
||||
urlTab ? setActiveTab(Number(urlTab)) : setActiveTab(1);
|
||||
}, [location])
|
||||
|
||||
return (
|
||||
<div role="tablist" className="tw-tabs tw-tabs-lifted tw-mt-4">
|
||||
<input type="radio" name="my_tabs_2" role="tab" className={`tw-tab [--tab-border-color:var(--fallback-bc,oklch(var(--bc)/0.2))]`} aria-label="Info" 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">
|
||||
<TextAreaInput placeholder="About me ..." defaultValue={item?.text ? item.text : ""} updateFormValue={(v) => setState(prevState => ({
|
||||
...prevState,
|
||||
text: v
|
||||
}))} containerStyle='tw-h-full' inputStyle='tw-h-full tw-border-t-0 tw-rounded-tl-none' />
|
||||
</div>
|
||||
{item.layer?.itemType.offers_and_needs &&
|
||||
<>
|
||||
<input type="radio" name="my_tabs_2" role="tab" className={`tw-tab tw-min-w-[10em] [--tab-border-color:var(--fallback-bc,oklch(var(--bc)/0.2))]`} aria-label="Offers & Needs" checked={activeTab == 3 && true} onChange={() => updateActiveTab(3)} />
|
||||
<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 className='tw-h-full'>
|
||||
<div className='tw-w-full tw-h-[calc(50%-0.75em)] tw-mb-4'>
|
||||
<TagsWidget defaultTags={state.offers} onUpdate={(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' />
|
||||
</div>
|
||||
<div className='tw-w-full tw-h-[calc(50%-0.75em)] '>
|
||||
<TagsWidget defaultTags={state.needs} onUpdate={(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' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
{item.layer?.itemType.relations &&
|
||||
<>
|
||||
<input type="radio" name="my_tabs_2" role="tab" className="tw-tab [--tab-border-color:var(--fallback-bc,oklch(var(--bc)/0.2))]" aria-label="Relations" checked={activeTab == 7 && true} onChange={() => updateActiveTab(7)} />
|
||||
<div role="tabpanel" 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">
|
||||
<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'>
|
||||
{state.relations && state.relations.map(i =>
|
||||
|
||||
|
||||
<div 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' onClick={() => navigate('/item/' + i.id)}>
|
||||
<LinkedItemsHeaderView unlinkPermission={updatePermission} item={i} unlinkCallback={(id) => unlinkItem(id, item, updateItem)} loading={loading} />
|
||||
<div className='tw-overflow-y-auto tw-overflow-x-hidden tw-max-h-64 fade'>
|
||||
<TextView truncate item={i} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{updatePermission && <ActionButton customStyle="!tw-bottom-20" collection="items" item={item} existingRelations={state.relations} triggerItemSelected={(id) => linkItem(id, item, updateItem)} colorField={item.layer.itemColorField}></ActionButton>}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -2,17 +2,28 @@ import { StartEndView, TextView } from '../../Map'
|
||||
import { TagView } from '../../Templates/TagView'
|
||||
import { LinkedItemsHeaderView } from '../Subcomponents/LinkedItemsHeaderView'
|
||||
import { ActionButton } from '../Subcomponents/ActionsButton'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useAddFilterTag } from '../../Map/hooks/useFilter'
|
||||
import { Item, Tag } from 'utopia-ui/dist/types'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
export const Tabs = ({ item, offers, needs, relations, updatePermission, loading, linkItem, unlinkItem }: { item: Item, offers: Array<Tag>, needs: Array<Tag>, relations: Array<Item>, updatePermission: boolean, loading: boolean, linkItem: (id: string) => Promise<void>, unlinkItem: (id: string) => Promise<void> }) => {
|
||||
export const TabsView = ({ item, offers, needs, relations, updatePermission, loading, linkItem, unlinkItem }: { item: Item, offers: Array<Tag>, needs: Array<Tag>, relations: Array<Item>, updatePermission: boolean, loading: boolean, linkItem: (id: string) => Promise<void>, unlinkItem: (id: string) => Promise<void> }) => {
|
||||
|
||||
const addFilterTag = useAddFilterTag();
|
||||
const [activeTab, setActiveTab] = useState<number>(1);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [addItemPopupType, setAddItemPopupType] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
scroll();
|
||||
}, [addItemPopupType])
|
||||
|
||||
function scroll() {
|
||||
tabRef.current?.scrollIntoView();
|
||||
}
|
||||
|
||||
const tabRef = useRef<HTMLFormElement>(null);
|
||||
|
||||
const updateActiveTab = (id: number) => {
|
||||
setActiveTab(id);
|
||||
@ -1,9 +1,10 @@
|
||||
import { Item } from '../../types';
|
||||
import { Item, Tag } from '../../types';
|
||||
import { encodeTag } from '../../Utils/FormatTags';
|
||||
import { hashTagRegex } from '../../Utils/HashTagRegex';
|
||||
import { randomColor } from '../../Utils/RandomColor';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
export const submitNewItem = async (evt: any, type: string, item, user, setLoading, tags, addTag, addItem, linkItem, resetFilterTags, layers, addItemPopupType, setAddItemPopupType,) => {
|
||||
export const submitNewItem = async (evt: any, type: string, item, user, setLoading, tags, addTag, addItem, linkItem, resetFilterTags, layers, addItemPopupType, setAddItemPopupType) => {
|
||||
evt.preventDefault();
|
||||
const formItem: Item = {} as Item;
|
||||
Array.from(evt.target).forEach((input: HTMLInputElement) => {
|
||||
@ -98,4 +99,109 @@ export const handleDelete = async (event: React.MouseEvent<HTMLElement>, item, s
|
||||
let params = new URLSearchParams(window.location.search);
|
||||
window.history.pushState({}, "", "/" + `${params ? `?${params}` : ""}`);
|
||||
navigate("/");
|
||||
}
|
||||
|
||||
|
||||
export const onUpdateItem = async (state, item, tags, addTag, setLoading, navigate, updateItem, addItem, user, params) => {
|
||||
let changedItem = {} as Item;
|
||||
|
||||
let offer_updates: Array<any> = [];
|
||||
//check for new offers
|
||||
state.offers?.map(o => {
|
||||
const existingOffer = item?.offers?.find(t => t.tags_id === o.id)
|
||||
existingOffer && offer_updates.push(existingOffer.id)
|
||||
if (!existingOffer && !tags.some(t => t.id === o.id)) addTag({ ...o, offer_or_need: true })
|
||||
!existingOffer && offer_updates.push({ items_id: item?.id, tags_id: o.id })
|
||||
});
|
||||
|
||||
let needs_updates: Array<any> = [];
|
||||
|
||||
state.needs?.map(n => {
|
||||
const existingNeed = item?.needs?.find(t => t.tags_id === n.id)
|
||||
existingNeed && needs_updates.push(existingNeed.id)
|
||||
!existingNeed && needs_updates.push({ items_id: item?.id, tags_id: n.id })
|
||||
!existingNeed && !tags.some(t => t.id === n.id) && addTag({ ...n, offer_or_need: true })
|
||||
});
|
||||
|
||||
|
||||
// update profile item in current state
|
||||
changedItem = {
|
||||
id: state.id,
|
||||
group_type: state.groupType,
|
||||
status: state.status,
|
||||
name: state.name,
|
||||
subname: state.subname,
|
||||
text: state.text,
|
||||
color: state.color,
|
||||
position: item.position,
|
||||
contact: state.contact,
|
||||
telephone: state.telephone,
|
||||
...state.markerIcon && { markerIcon: state.markerIcon },
|
||||
next_appointment: state.nextAppointment,
|
||||
...state.image.length > 10 && { image: state.image },
|
||||
...state.offers.length > 0 && { offers: offer_updates },
|
||||
...state.needs.length > 0 && { needs: needs_updates }
|
||||
};
|
||||
|
||||
let offers_state: Array<any> = [];
|
||||
let needs_state: Array<any> = [];
|
||||
|
||||
await state.offers.map(o => {
|
||||
offers_state.push({ items_id: item?.id, tags_id: o.id })
|
||||
});
|
||||
|
||||
await state.needs.map(n => {
|
||||
needs_state.push({ items_id: item?.id, tags_id: n.id })
|
||||
});
|
||||
|
||||
changedItem = { ...changedItem, offers: offers_state, needs: needs_state };
|
||||
|
||||
|
||||
state.text.toLocaleLowerCase().match(hashTagRegex)?.map(tag => {
|
||||
if (!tags.find((t) => t.name.toLocaleLowerCase() === tag.slice(1).toLocaleLowerCase())) {
|
||||
addTag({ id: crypto.randomUUID(), name: encodeTag(tag.slice(1).toLocaleLowerCase()), color: randomColor() })
|
||||
}
|
||||
});
|
||||
|
||||
setLoading(true);
|
||||
console.log(item.layer);
|
||||
|
||||
|
||||
if (!item.new) {
|
||||
item?.layer?.api?.updateItem && toast.promise(
|
||||
item?.layer?.api?.updateItem(changedItem),
|
||||
{
|
||||
pending: 'updating Item ...',
|
||||
success: 'Item updated',
|
||||
error: {
|
||||
render({ data }) {
|
||||
return `${data}`
|
||||
},
|
||||
},
|
||||
})
|
||||
.then(() => item && updateItem({ ...item, ...changedItem }))
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
navigate(`/item/${item.id}${params && "?" + params}`)
|
||||
});
|
||||
|
||||
}
|
||||
else {
|
||||
item.layer?.api?.createItem && toast.promise(
|
||||
item.layer?.api?.createItem(changedItem),
|
||||
{
|
||||
pending: 'updating Item ...',
|
||||
success: 'Item updated',
|
||||
error: {
|
||||
render({ data }) {
|
||||
return `${data}`
|
||||
},
|
||||
},
|
||||
})
|
||||
.then(() => item && addItem({ ...item, ...changedItem, layer: item.layer, user_created: user, type: item.layer?.itemType }))
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
navigate(`/${params && "?" + params}`)
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user