mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
merged
This commit is contained in:
commit
f23ea88e31
@ -0,0 +1,29 @@
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useAuth } from "../../../Auth";
|
||||
|
||||
export const GratitudeControl = () => {
|
||||
const navigate = useNavigate();
|
||||
const {isAuthenticated} = useAuth();
|
||||
|
||||
if(isAuthenticated) return (
|
||||
<div className="tw-card tw-bg-base-100 tw-shadow-xl tw-mt-2 tw-w-fit">
|
||||
{
|
||||
|
||||
|
||||
<div className="tw-card-body hover:tw-bg-slate-300 tw-card tw-p-2 tw-h-10 tw-w-10 tw-transition-all tw-duration-300 hover:tw-cursor-pointer" onClick={() => {
|
||||
navigate("/select-user")
|
||||
}}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={2.5} stroke="currentColor" className="size-6">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12Z" />
|
||||
</svg>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
}
|
||||
|
||||
</div>
|
||||
)
|
||||
else return (<></>);
|
||||
}
|
||||
@ -9,9 +9,8 @@ import AddButton from "./Subcomponents/AddButton";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ItemFormPopupProps } from "./Subcomponents/ItemFormPopup";
|
||||
import { SearchControl } from "./Subcomponents/Controls/SearchControl";
|
||||
// import { QuestControl } from "./Subcomponents/Controls/QuestControl";
|
||||
import { Control } from "./Subcomponents/Controls/Control";
|
||||
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||
import { Outlet } from "react-router-dom";
|
||||
import { TagsControl } from "./Subcomponents/Controls/TagsControl";
|
||||
import { useSelectPosition, useSetMapClicked, useSetSelectPosition } from "./hooks/useSelectPosition";
|
||||
import { useClusterRef, useSetClusterRef } from "./hooks/useClusterRef";
|
||||
@ -20,6 +19,7 @@ import { FilterControl } from "./Subcomponents/Controls/FilterControl";
|
||||
import { LayerControl } from "./Subcomponents/Controls/LayerControl";
|
||||
import { useLayers } from "./hooks/useLayers";
|
||||
import { useAddVisibleLayer } from "./hooks/useFilter";
|
||||
import { GratitudeControl } from "./Subcomponents/Controls/GratitudeControl";
|
||||
import { SelectPosition } from "./Subcomponents/SelectPosition";
|
||||
|
||||
// for refreshing map on resize (needs to be implemented)
|
||||
@ -63,21 +63,13 @@ function UtopiaMap({
|
||||
|
||||
const selectNewItemPosition = useSelectPosition();
|
||||
const setSelectNewItemPosition = useSetSelectPosition();
|
||||
const location = useLocation();
|
||||
const setClusterRef = useSetClusterRef();
|
||||
const clusterRef = useClusterRef();
|
||||
const setMapClicked = useSetMapClicked();
|
||||
|
||||
const [itemFormPopup, setItemFormPopup] = useState<ItemFormPopupProps | null>(null);
|
||||
|
||||
const [embedded, setEmbedded] = useState<boolean>(true)
|
||||
|
||||
useEffect(() => {
|
||||
let params = new URLSearchParams(location.search);
|
||||
let urlPosition = params.get("position");
|
||||
let embedded = params.get("embedded");
|
||||
embedded != "true" && setEmbedded(false)
|
||||
}, [location]);
|
||||
|
||||
|
||||
const layers = useLayers();
|
||||
@ -113,6 +105,7 @@ function UtopiaMap({
|
||||
{showFilterControl && <FilterControl />}
|
||||
{/*todo: needed layer handling is located LayerControl*/}
|
||||
{showLayerControl && <LayerControl></LayerControl>}
|
||||
{<GratitudeControl/>}
|
||||
</Control>
|
||||
<TileLayer
|
||||
maxZoom={19}
|
||||
|
||||
@ -2,7 +2,7 @@ import { MapOverlayPage } from '../Templates'
|
||||
import { useItems, useRemoveItem, useUpdateItem } from '../Map/hooks/useItems'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Item, Tag } from '../../types';
|
||||
import { Item, ItemsApi, Tag } from '../../types';
|
||||
import { useMap } from 'react-leaflet';
|
||||
import { LatLng } from 'leaflet';
|
||||
import { useHasUserPermission } from '../Map/hooks/usePermissions';
|
||||
@ -17,7 +17,7 @@ import { SimpleView } from './Templates/SimpleView';
|
||||
import { handleDelete, linkItem, unlinkItem } from './itemFunctions';
|
||||
import { useTags } from '../Map/hooks/useTags';
|
||||
|
||||
export function ProfileView({ userType }: { userType: string }) {
|
||||
export function ProfileView({ userType, attestationApi }: { userType: string , attestationApi?: ItemsApi<any>}) {
|
||||
|
||||
const [item, setItem] = useState<Item>()
|
||||
const [updatePermission, setUpdatePermission] = useState<boolean>(false);
|
||||
@ -40,6 +40,23 @@ export function ProfileView({ userType }: { userType: string }) {
|
||||
const clusterRef = useClusterRef();
|
||||
const leafletRefs = useLeafletRefs();
|
||||
|
||||
const [attestations, setAttestations] = useState<Array<any>>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (attestationApi) {
|
||||
attestationApi.getItems()
|
||||
.then(value => {
|
||||
console.log(value);
|
||||
|
||||
setAttestations(value);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error fetching items:", error);
|
||||
});
|
||||
}
|
||||
}, [attestationApi])
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const itemId = location.pathname.split("/")[2];
|
||||
const item = items.find(i => i.id === itemId);
|
||||
@ -140,7 +157,7 @@ export function ProfileView({ userType }: { userType: string }) {
|
||||
}
|
||||
|
||||
{template == "tabs" &&
|
||||
<TabsView setUrlParams={setUrlParams} 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 userType={userType} attestations={attestations} setUrlParams={setUrlParams} item={item} loading={loading} offers={offers} needs={needs} relations={relations} updatePermission={updatePermission} linkItem={(id) => linkItem(id, item, updateItem)} unlinkItem={(id) => unlinkItem(id, item, updateItem)}/>
|
||||
}
|
||||
</>
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ export const TabsForm = ({ item, state, setState, updatePermission, linkItem, un
|
||||
setActiveTab(id);
|
||||
|
||||
let params = new URLSearchParams(window.location.search);
|
||||
|
||||
params.set("tab", `${id}`);
|
||||
const newUrl = location.pathname + "?" + params.toString();
|
||||
window.history.pushState({}, '', newUrl);
|
||||
|
||||
@ -5,9 +5,13 @@ import { ActionButton } from '../Subcomponents/ActionsButton'
|
||||
import { useCallback, 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'
|
||||
import { Link, useNavigate } from 'react-router-dom'
|
||||
import { useItems } from '../../Map/hooks/useItems'
|
||||
import { useAssetApi } from '../../AppShell/hooks/useAssets'
|
||||
import { timeAgo } from '../../../Utils/TimeAgo'
|
||||
import { useAuth } from '../../Auth'
|
||||
|
||||
export const TabsView = ({ item, offers, needs, relations, updatePermission, loading, linkItem, unlinkItem, setUrlParams }: { 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> , setUrlParams: any}) => {
|
||||
export const TabsView = ({ attestations, userType, item, offers, needs, relations, updatePermission, loading, linkItem, unlinkItem, setUrlParams }: { attestations: Array<any>, userType: string, 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>, setUrlParams: any }) => {
|
||||
|
||||
const addFilterTag = useAddFilterTag();
|
||||
const [activeTab, setActiveTab] = useState<number>();
|
||||
@ -15,12 +19,18 @@ export const TabsView = ({ item, offers, needs, relations, updatePermission, loa
|
||||
|
||||
const [addItemPopupType, setAddItemPopupType] = useState<string>("");
|
||||
|
||||
const items = useItems();
|
||||
const assetsApi = useAssetApi();
|
||||
const getUserProfile = (id: string) => {
|
||||
return items.find(i => i.user_created.id === id && i.layer?.itemType.name === userType)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
scroll();
|
||||
scroll();
|
||||
}, [addItemPopupType])
|
||||
|
||||
|
||||
function scroll() {
|
||||
tabRef.current?.scrollIntoView();
|
||||
tabRef.current?.scrollIntoView();
|
||||
}
|
||||
|
||||
const tabRef = useRef<HTMLFormElement>(null);
|
||||
@ -46,7 +56,7 @@ export const TabsView = ({ item, offers, needs, relations, updatePermission, loa
|
||||
<div role="tablist" className="tw-tabs tw-tabs-lifted tw-mt-2 tw-mb-2 tw-px-6">
|
||||
<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}
|
||||
aria-label="👤" checked={activeTab == 1 && true}
|
||||
onChange={() => updateActiveTab(1)} />
|
||||
<div role="tabpanel"
|
||||
className="tw-tab-content tw-bg-base-100 tw-rounded-box tw-h-[calc(100dvh-280px)] tw-overflow-y-auto fade tw-pt-2 tw-pb-4 tw-mb-4 tw-overflow-x-hidden">
|
||||
@ -55,14 +65,63 @@ export const TabsView = ({ item, offers, needs, relations, updatePermission, loa
|
||||
}
|
||||
<TextView item={item} />
|
||||
<div className='tw-h-4'></div>
|
||||
<TextView item={item} itemTextField='contact'/>
|
||||
<TextView item={item} itemTextField='contact' />
|
||||
</div>
|
||||
{item.layer?.itemType.questlog &&
|
||||
<>
|
||||
<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="❤️" checked={activeTab == 2 && true}
|
||||
onChange={() => updateActiveTab(2)} />
|
||||
|
||||
<div role="tabpanel"
|
||||
className="tw-tab-content tw-bg-base-100 tw-rounded-box tw-h-[calc(100dvh-280px)] tw-overflow-y-auto fade tw-pt-2 tw-pb-4 tw-mb-4 tw-overflow-x-hidden">
|
||||
<table className="sm:tw-table-sm md:tw-table-md">
|
||||
<tbody>
|
||||
{attestations
|
||||
.filter(a => a.to.some(t => t.directus_users_id == item.user_created.id))
|
||||
.sort((a, b) => new Date(b.date_created).getTime() - new Date(a.date_created).getTime())
|
||||
.map((a, i) => (
|
||||
<tr key={i}>
|
||||
<td>
|
||||
<div className={`tw-cursor-pointer tw-text-3xl tw-mask tw-mask-${a.shape} tw-p-3 tw-mr-2 tw-shadow-xl tw-bg-[${a.color}]`}>
|
||||
{a.emoji}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div className='tw-mr-2'><i>{a.text}</i></div>
|
||||
</td>
|
||||
<td>
|
||||
<Link to={"/item/" + getUserProfile(a.user_created.id)?.id}>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="tw-avatar">
|
||||
<div className="tw-mask tw-rounded-full h-8 w-8 tw-mr-2">
|
||||
<img
|
||||
src={assetsApi.url + getUserProfile(a.user_created.id)?.image}
|
||||
alt="Avatar Tailwind CSS Component"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold">{getUserProfile(a.user_created.id)?.name}</div>
|
||||
<div className="tw-text-xs opacity-50 tw-text-zinc-500">{timeAgo(a.date_created)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</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)} />
|
||||
<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="♻️" checked={activeTab == 3 && true} onChange={() => updateActiveTab(3)} />
|
||||
<div role="tabpanel" className="tw-tab-content tw-bg-base-100 tw-rounded-box tw-h-[calc(100dvh-268px)] tw-overflow-y-auto fade tw-pt-4 tw-pb-1" >
|
||||
<div className='tw-h-full'>
|
||||
<div className='tw-grid tw-grid-cols-1'>
|
||||
@ -100,7 +159,7 @@ export const TabsView = ({ item, offers, needs, relations, updatePermission, loa
|
||||
|
||||
{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)} />
|
||||
<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="🔗" 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-280px)] tw-overflow-y-auto tw-pt-4 tw-pb-1 -tw-mr-4 -tw-mb-4 tw-overflow-x-hidden">
|
||||
<div className='tw-h-full'>
|
||||
<div className='tw-grid tw-grid-cols-1 sm:tw-grid-cols-2 md:tw-grid-cols-1 lg:tw-grid-cols-1 xl:tw-grid-cols-1 2xl:tw-grid-cols-2 tw-pb-4'>
|
||||
|
||||
108
src/Components/Templates/AttestationForm.tsx
Normal file
108
src/Components/Templates/AttestationForm.tsx
Normal file
@ -0,0 +1,108 @@
|
||||
|
||||
import { MapOverlayPage } from './MapOverlayPage'
|
||||
import { useItems } from '../Map/hooks/useItems'
|
||||
import { useAssetApi } from '../AppShell/hooks/useAssets'
|
||||
import { EmojiPicker } from './EmojiPicker';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { useRef, useState } from 'react';
|
||||
import { Item, ItemsApi } from '../../types';
|
||||
import { useEffect } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
export const AttestationForm = ({api}:{api?:ItemsApi<any>}) => {
|
||||
|
||||
const items = useItems();
|
||||
const assetsApi = useAssetApi();
|
||||
const [users, setUsers] = useState<Array<Item>>();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
let params = new URLSearchParams(location.search);
|
||||
let to_user_ids = params.get("to");
|
||||
setUsers(items.filter(i => to_user_ids?.includes(i.id)))
|
||||
}, [items, location])
|
||||
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (inputRef.current) {
|
||||
inputRef.current.style.width = 'auto';
|
||||
inputRef.current.style.width = `${inputRef.current.scrollWidth+20}px`;
|
||||
}
|
||||
}, [inputValue]);
|
||||
|
||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setInputValue(event.target.value);
|
||||
};
|
||||
|
||||
const sendAttestation = async () => {
|
||||
const to : Array<any> = [];
|
||||
users?.map(u => to.push({ directus_users_id: u.user_created.id }));
|
||||
|
||||
|
||||
api?.createItem && toast.promise(
|
||||
api.createItem({
|
||||
text: inputValue,
|
||||
emoji: selectedEmoji,
|
||||
color: selectedColor,
|
||||
shape: selectedShape,
|
||||
to: to
|
||||
}), {
|
||||
pending: 'creating attestation ...',
|
||||
success: 'Attestation created',
|
||||
error: {
|
||||
render({ data }) {
|
||||
return `${data}`
|
||||
},
|
||||
},
|
||||
})
|
||||
.then( () => navigate("/item/" + items.find(i => i.user_created.id===to[0].directus_users_id && i.layer?.itemType.name==="player")?.id+"?tab=2")
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
const [selectedEmoji, setSelectedEmoji] = useState('select badge');
|
||||
const [selectedShape, setSelectedShape] = useState('circle');
|
||||
const [selectedColor, setSelectedColor] = useState('#fff0d6');
|
||||
|
||||
|
||||
return (
|
||||
<MapOverlayPage backdrop className='tw-h-fit tw-min-h-56 tw-w-96'>
|
||||
<div className='tw-text-center tw-text-xl tw-font-bold'>Gratitude</div>
|
||||
<div className='tw-text-center tw-text-base tw-text-gray-400'>to</div>
|
||||
<div className='tw-flex tw-flex-row tw-justify-center tw-items-center tw-flex-wrap'>
|
||||
{users && users.map((u, k) => (
|
||||
<div key={k} className="tw-flex tw-items-center tw-space-x-3 tw-mx-2 tw-my-1">
|
||||
{u.image ? <div className="tw-avatar">
|
||||
<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" />
|
||||
</div>
|
||||
</div> :
|
||||
<div className='tw-mask tw-mask-circle tw-text-xl md:tw-text-2xl tw-bg-slate-200 tw-rounded-full tw-w-8 tw-h-8'></div>}
|
||||
<div>
|
||||
<div className="tw-font-bold">{u.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
), ", ")}
|
||||
</div>
|
||||
|
||||
<div className='tw-w-full'>
|
||||
<div className='tw-flex tw-justify-center tw-items-center'>
|
||||
<div className=' tw-flex tw-justify-center tw-items-center tw-w-28 tw-h-28 tw-m-4'>
|
||||
<EmojiPicker selectedEmoji={selectedEmoji} selectedColor={selectedColor} selectedShape={selectedShape} setSelectedEmoji={setSelectedEmoji} setSelectedColor={setSelectedColor} setSelectedShape={setSelectedShape} />
|
||||
</div>
|
||||
</div>
|
||||
<div className='tw-flex tw-justify-center tw-items-center'>
|
||||
<input ref={inputRef}
|
||||
value={inputValue}
|
||||
onChange={handleChange}
|
||||
type="text"
|
||||
placeholder="... and say some words"
|
||||
className="tw-input tw-min-w-0 tw-w-fit tw-resize-none tw-overflow-hidden tw-text-center " />
|
||||
</div>
|
||||
</div>
|
||||
<div className='tw-w-full tw-grid tw-mt-4'><button onClick={sendAttestation} className="tw-btn tw-place-self-center tw-px-8">Next</button></div>
|
||||
</MapOverlayPage>
|
||||
)
|
||||
}
|
||||
93
src/Components/Templates/EmojiPicker.tsx
Normal file
93
src/Components/Templates/EmojiPicker.tsx
Normal file
@ -0,0 +1,93 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
export const EmojiPicker = ({selectedEmoji, selectedColor, selectedShape, setSelectedEmoji, setSelectedColor, setSelectedShape}) => {
|
||||
|
||||
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const emojis = [
|
||||
'❤️', '🙏', '👍', '🌻',, '✨', '☀️',
|
||||
'🔥', '🪵', '💧', '🎶', '🎨','🍄',
|
||||
'📝', '✉️', '🧩','💡', '🎓', '💬',
|
||||
'🛠', '💻', '🕹', '🖨', '🚐', '🛒',
|
||||
'⚽️', '🧵', '👀', '🌱',
|
||||
'🏕', '💪', '🎁', '🏹',
|
||||
'🥕', '🥇', '🥈', '🥉'];
|
||||
const shapes = ["squircle", "circle", "hexagon-2"];
|
||||
|
||||
const colors = ["#FF99C8", "#fff0d6", "#FCF6BD", "#D0F4DE", "#A9DEF9", "#E4C1F9", "#de324c", "#f4895f", "#f8e16f", "#95cf92", "#369acc", "#9656a2"]
|
||||
|
||||
|
||||
const toggleDropdown = () => {
|
||||
setIsOpen(!isOpen);
|
||||
};
|
||||
|
||||
const selectEmoji = (emoji) => {
|
||||
setSelectedEmoji(emoji);
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
const selectShape = (shape) => {
|
||||
setSelectedShape(shape);
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
const selectColor = (color) => {
|
||||
setSelectedColor(color);
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
onClick={toggleDropdown}
|
||||
className={`tw-cursor-pointer ${selectedEmoji=="select badge" ? 'tw-text-sm !tw-p-9 tw-text-center ' : 'tw-text-6xl'} tw-mask tw-mask-${selectedShape} tw-p-6 tw-bg-[${selectedColor}]`}
|
||||
>
|
||||
{selectedEmoji}
|
||||
</div>
|
||||
|
||||
{isOpen && (
|
||||
<div className="tw-absolute tw-z-3000 tw-top-0 tw-left-1/2 tw-transform tw--translate-x-1/2 tw-mt-12 tw-bg-base-100 tw-rounded-2xl tw-shadow-lg tw-p-2 tw-w-full">
|
||||
<div className='tw-grid tw-grid-cols-6 tw-gap-2 tw-pb-2'>
|
||||
{emojis.map((emoji) => (
|
||||
<button
|
||||
key={emoji}
|
||||
onClick={() => selectEmoji(emoji)}
|
||||
className={`tw-cursor-pointer tw-text-2xl tw-p-2 hover:tw-bg-base-200 tw-rounded-md ${emoji == selectedEmoji && "tw-bg-base-300"}`}
|
||||
>
|
||||
{emoji}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<hr />
|
||||
<div className='tw-grid tw-grid-cols-3 tw-gap-2 tw-py-2'>
|
||||
{shapes.map(shape => (
|
||||
<div
|
||||
key={shape}
|
||||
className={`tw-cursor-pointer hover:tw-bg-base-200 tw-rounded-md tw-p-2 ${shape == selectedShape && "tw-bg-base-300"}`}
|
||||
onClick={() => selectShape(shape)}>
|
||||
<div className={`tw-h-12 tw-mask tw-mask-${shape} tw-bg-neutral-content`}></div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<hr />
|
||||
<div className='tw-grid tw-grid-cols-6 tw-gap-2 tw-py-2 tw-px-6'>
|
||||
{colors.map(color => (
|
||||
<div
|
||||
key={color}
|
||||
className={`tw-cursor-pointer hover:tw-bg-base-200 tw-rounded-md tw-p-2 tw-flex tw-justify-center tw-items-center ${color == selectedColor && "tw-bg-base-300"}`}
|
||||
onClick={() => selectColor(color)}>
|
||||
<div className={`tw-h-8 tw-w-8 tw-rounded-full tw-bg-[${color}]`}></div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
55
src/Components/Templates/SelectUser.tsx
Normal file
55
src/Components/Templates/SelectUser.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import { useState } from 'react'
|
||||
import { MapOverlayPage } from './MapOverlayPage'
|
||||
import { useItems } from '../Map/hooks/useItems'
|
||||
import { useAssetApi } from '../AppShell/hooks/useAssets'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
export const SelectUser = ({ userType }: { userType: string }) => {
|
||||
|
||||
const items = useItems();
|
||||
const users = items.filter(i => i.layer?.itemType.name == userType)
|
||||
const assetsApi = useAssetApi();
|
||||
|
||||
const [selectedUsers, setSelectedUsers] = useState<Array<string>>([]);
|
||||
|
||||
return (
|
||||
<MapOverlayPage backdrop className='tw-h-3/4 tw-w-80'>
|
||||
|
||||
<div className='tw-text-center tw-text-xl tw-font-bold tw-mb-4'>Gratitude to ...</div>
|
||||
|
||||
|
||||
{/* Team Member list in table format loaded constant */}
|
||||
<div className="tw-overflow-x-auto tw-w-full fade">
|
||||
<table className="tw-table tw-w-full">
|
||||
<tbody>
|
||||
{
|
||||
users.map((u, k) => {
|
||||
return (
|
||||
<tr key={k}>
|
||||
<td>
|
||||
<input type="checkbox" onChange={() => setSelectedUsers(prev => [...prev, u.id])} className="tw-checkbox tw-checkbox-sm" />
|
||||
</td>
|
||||
<td>
|
||||
<div className="tw-flex tw-items-center tw-space-x-3">
|
||||
{u.image ? <div className="tw-avatar">
|
||||
<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" />
|
||||
</div>
|
||||
</div> :
|
||||
<div className='tw-mask tw-mask-circle tw-text-xl md:tw-text-2xl tw-bg-slate-200 tw-rounded-full tw-w-8 tw-h-8'></div>}
|
||||
<div>
|
||||
<div className="tw-font-bold">{u.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className='tw-w-full tw-grid tw-mt-4'><Link className="tw-place-self-center " to={"/attestation-form"+"?to="+selectedUsers.map(u=> u,",")}><button className="tw-btn tw-px-8">Next</button></Link></div>
|
||||
</MapOverlayPage>
|
||||
)
|
||||
}
|
||||
@ -1,5 +1,9 @@
|
||||
import { AttestationForm } from './AttestationForm'
|
||||
|
||||
export {CardPage} from './CardPage'
|
||||
export {TitleCard} from './TitleCard'
|
||||
export {MapOverlayPage} from './MapOverlayPage'
|
||||
export {MoonCalendar} from './MoonCalendar'
|
||||
export {OverlayItemsIndexPage} from "./OverlayItemsIndexPage"
|
||||
export {SelectUser} from "./SelectUser"
|
||||
export {OverlayItemsIndexPage} from "./OverlayItemsIndexPage"
|
||||
export {AttestationForm} from "./AttestationForm"
|
||||
|
||||
@ -52,6 +52,10 @@
|
||||
|
||||
}
|
||||
|
||||
.placeholder-center::placeholder {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.custom-file-upload {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ export {AppShell, Content, SideBar, Sitemap } from "./Components/AppShell"
|
||||
export {AuthProvider, useAuth, LoginPage, SignupPage, RequestPasswordPage, SetNewPasswordPage} from "./Components/Auth"
|
||||
export {UserSettings, ProfileView, ProfileForm} from './Components/Profile'
|
||||
export {Quests, Modal} from './Components/Gaming'
|
||||
export {TitleCard, CardPage, MapOverlayPage, OverlayItemsIndexPage, MoonCalendar } from './Components/Templates'
|
||||
export {TitleCard, CardPage, MapOverlayPage, OverlayItemsIndexPage, MoonCalendar, SelectUser, AttestationForm } from './Components/Templates'
|
||||
export {TextInput, TextAreaInput, SelectBox} from './Components/Input'
|
||||
|
||||
import "./index.css"
|
||||
|
||||
@ -4,6 +4,25 @@ module.exports = {
|
||||
"./src/**/*.{js,jsx,ts,tsx}",
|
||||
"./node_modules/tw-elements/dist/js/**/*.js"
|
||||
],
|
||||
safelist: [
|
||||
'tw-mask-squircle',
|
||||
'tw-mask-circle',
|
||||
'tw-mask-hexagon-2',
|
||||
'tw-mask-decagon',
|
||||
'tw-bg-[#FF99C8]',
|
||||
'tw-bg-[#fff0d6]',
|
||||
'tw-bg-[#FCF6BD]',
|
||||
'tw-bg-[#D0F4DE]',
|
||||
'tw-bg-[#A9DEF9]',
|
||||
'tw-bg-[#E4C1F9]',
|
||||
'tw-bg-[#de324c]',
|
||||
'tw-bg-[#f4895f]',
|
||||
'tw-bg-[#f8e16f]',
|
||||
'tw-bg-[#95cf92]',
|
||||
'tw-bg-[#369acc]',
|
||||
'tw-bg-[#9656a2]',
|
||||
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
zIndex: {
|
||||
@ -31,7 +50,7 @@ module.exports = {
|
||||
'sans': ["Helvetica", "sans-serif", 'Roboto'],
|
||||
},
|
||||
fontSize: {
|
||||
'map': "13px"
|
||||
'map': "13px"
|
||||
},
|
||||
lineHeight: {
|
||||
'map': "1.4em"
|
||||
@ -54,17 +73,18 @@ module.exports = {
|
||||
],
|
||||
daisyui: {
|
||||
themes: ["light", "dark", "cupcake", "retro", "cyberpunk", "aqua",
|
||||
{
|
||||
docutopia: {
|
||||
"primary": "#8e00ff",
|
||||
"secondary": "#00bb7a",
|
||||
"accent": "#006aff",
|
||||
"neutral": "#231502",
|
||||
"base-content": "#ffad6b",
|
||||
"base-100": "#440844",
|
||||
},
|
||||
},]
|
||||
{
|
||||
docutopia: {
|
||||
"primary": "#8e00ff",
|
||||
"secondary": "#00bb7a",
|
||||
"accent": "#006aff",
|
||||
"neutral": "#231502",
|
||||
"base-content": "#ffad6b",
|
||||
"base-100": "#440844",
|
||||
},
|
||||
},]
|
||||
},
|
||||
prefix: 'tw-',
|
||||
content: ['./src/**/*.{js,jsx,ts,tsx}'],
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user