144 lines
4.3 KiB
Svelte
144 lines
4.3 KiB
Svelte
<script lang="ts">
|
|
|
|
import type { Item } from '$lib/types/item';
|
|
import { Badge } from '$lib/components/ui/badge';
|
|
import LocationIcon from '@lucide/svelte/icons/map-pinned';
|
|
import CheckIcon from '@lucide/svelte/icons/check';
|
|
import XIcon from '@lucide/svelte/icons/x';
|
|
import PencilIcon from '@lucide/svelte/icons/pencil';
|
|
import NotebookPenIcon from '@lucide/svelte/icons/notebook-pen';
|
|
import TrashIcon from '@lucide/svelte/icons/trash';
|
|
import StarIcon from '@lucide/svelte/icons/star';
|
|
import ArchiveRestoreIcon from '@lucide/svelte/icons/archive-restore';
|
|
import { Button } from '$lib/components/ui/button';
|
|
import * as Tooltip from '$lib/components/ui/tooltip';
|
|
import { dateFormatOptions } from '$lib/shared';
|
|
import { approveDenyItem, restoreClaimedItem } from '$lib/db/items.remote';
|
|
import { invalidateAll } from '$app/navigation';
|
|
import NoImagePlaceholder from './no-image-placeholder.svelte';
|
|
|
|
export let item: Item = <Item>{};
|
|
export let admin = false;
|
|
export let editCallback: (item: Item) => void;
|
|
export let inquireCallback: (item: Item) => void;
|
|
export let claimCallback: (item: Item) => void;
|
|
|
|
let timeSincePosted: number | string = (new Date().getTime() - item.foundDate.getTime()) / 1000 / 60 / 60 / 24; // days
|
|
if (timeSincePosted < 1) {
|
|
timeSincePosted = '<1';
|
|
} else {
|
|
timeSincePosted = Math.round(timeSincePosted);
|
|
}
|
|
|
|
</script>
|
|
|
|
<div
|
|
class="h-full bg-card text-card-foreground flex flex-col gap-2 rounded-xl border shadow-sm max-w-sm overflow-hidden min-w-2xs">
|
|
{#if item.image}
|
|
<img src="https://fbla26.marinodev.com/uploads/{item.id}.jpg" class="object-cover min-h-48 max-h-48"
|
|
alt="Lost item">
|
|
{:else}
|
|
<div class="min-h-48 w-full bg-accent flex flex-col justify-center">
|
|
|
|
<div class="justify-center flex ">
|
|
|
|
<NoImagePlaceholder className="" />
|
|
</div>
|
|
<p class="text-center mt-4">No image available</p>
|
|
</div>
|
|
{/if}
|
|
<div class="flex-col flex h-full px-2 pb-2">
|
|
|
|
<!-- <div class="font-bold inline-block">{item.title}</div>-->
|
|
<!-- <div class="inline-block">-->
|
|
<div>
|
|
|
|
{#if item.transferred}
|
|
<Badge variant="secondary" class="inline-block">In Lost & Found</Badge>
|
|
{:else}
|
|
<Badge variant="secondary" class="inline-block">With Finder</Badge>
|
|
{/if}
|
|
<Tooltip.Provider>
|
|
<Tooltip.Root>
|
|
<Tooltip.Trigger
|
|
>
|
|
<Badge variant="outline" class="inline-block">{timeSincePosted}
|
|
day{(timeSincePosted === 1 || timeSincePosted === '<1') ? '' : 's'} ago
|
|
</Badge>
|
|
</Tooltip.Trigger
|
|
>
|
|
<Tooltip.Content>
|
|
<p>{item.foundDate.toLocaleDateString('en-US', dateFormatOptions)}</p>
|
|
</Tooltip.Content>
|
|
</Tooltip.Root>
|
|
</Tooltip.Provider>
|
|
|
|
</div>
|
|
|
|
<div class="flex-1">{item.description}</div>
|
|
{#if item.foundLocation}
|
|
<div class="mt-2">
|
|
<LocationIcon class="float-left mr-1" size={24} />
|
|
<div>{item.foundLocation}</div>
|
|
</div>
|
|
{/if}
|
|
|
|
{#if admin}
|
|
<div class="mt-2 justify-between flex">
|
|
{#if item.approvedDate === null}
|
|
|
|
<Button variant="ghost" class="text-positive"
|
|
onclick={async () => {await approveDenyItem({id: item.id, approved: true});
|
|
invalidateAll()}}>
|
|
<CheckIcon />
|
|
Approve
|
|
</Button>
|
|
<Button variant="ghost" class="text-destructive"
|
|
onclick={async () => {await approveDenyItem({id: item.id, approved: false});
|
|
invalidateAll()}}>
|
|
<XIcon />
|
|
Deny
|
|
</Button>
|
|
{/if}
|
|
{#if item.claimedDate === null}
|
|
<Button variant="ghost" class="text-action"
|
|
onclick={() => {editCallback(item)}}>
|
|
<PencilIcon />
|
|
Manage
|
|
</Button>
|
|
{:else}
|
|
<Button variant="ghost" class="text-destructive"
|
|
onclick={async () => {await approveDenyItem({id: item.id, approved: false});
|
|
invalidateAll()}}>
|
|
<TrashIcon />
|
|
Delete
|
|
</Button>
|
|
<Button variant="ghost" class="text-action"
|
|
onclick={async () => {await restoreClaimedItem(item.id);
|
|
invalidateAll()}}>
|
|
<ArchiveRestoreIcon />
|
|
Restore
|
|
</Button>
|
|
{/if}
|
|
|
|
</div>
|
|
{:else}
|
|
<div class="mt-2 justify-between flex">
|
|
<Button variant="ghost" class="text-action"
|
|
onclick={() => {inquireCallback(item)}}>
|
|
<NotebookPenIcon />
|
|
Inquire
|
|
</Button>
|
|
<Button variant="ghost" class="text-primary"
|
|
onclick={() => {claimCallback(item)}}>
|
|
<StarIcon />
|
|
Claim
|
|
</Button>
|
|
</div>
|
|
{/if}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|