feat: share links visible on plants/containers; container image gallery
- Fix ShareButton silently swallowing errors from createShareLink; now shows inline error text so failures are visible to the user - Add getShareLinksForEntity server action and EntityShareLink type to _core/share.ts - Add ShareLinkList component — renders active share links per entity with per-row Revoke; renders nothing when empty - Wire ShareLinkList into plant and container detail pages (loaded server-side in parallel with the entity fetch) - Add images jsonb column to garden_containers schema + migration 0018 - Add addContainerImage / removeContainerImage / setContainerPrimaryImage server actions mirroring the plant image pattern (10-image cap, first upload auto-sets cover) - Update ContainerDetailDto, listContainers, getContainer to include images - Rewrite ContainerDetail with Info/Gallery tabs; Gallery tab mirrors plant gallery (3-col grid, star/X overlays, upload button, counter) - Update ContainerShareData and container renderSharedView to show cover image hero and secondary image grid on public share pages
This commit is contained in:
@@ -7,6 +7,8 @@ import { deletePlant, addPlantImage, removePlantImage, setPrimaryImage } from ".
|
||||
import type { CalendarDto } from "../server/calendar-bridge";
|
||||
import type { CareLogDto, CareScheduleDto, PlantDetailDto } from "../server/queries";
|
||||
import { ShareButton } from "@/components/share-button";
|
||||
import { ShareLinkList } from "@/components/share-link-list";
|
||||
import type { EntityShareLink } from "@/modules/_core/share";
|
||||
import { CareHistoryList } from "./care-history-list";
|
||||
import { CareLogForm } from "./care-log-form";
|
||||
import { CareScheduleEditor } from "./care-schedule-editor";
|
||||
@@ -18,6 +20,7 @@ type Props = {
|
||||
careLogs: CareLogDto[];
|
||||
careSchedules: CareScheduleDto[];
|
||||
calendars: CalendarDto[];
|
||||
shareLinks: EntityShareLink[];
|
||||
};
|
||||
|
||||
function InfoRow({
|
||||
@@ -44,7 +47,7 @@ function healthBadgeClass(status: string): string {
|
||||
return "badge-warning";
|
||||
}
|
||||
|
||||
export function PlantDetail({ plant, careLogs, careSchedules, calendars }: Props) {
|
||||
export function PlantDetail({ plant, careLogs, careSchedules, calendars, shareLinks }: Props) {
|
||||
const [tab, setTab] = useState<Tab>("info");
|
||||
const [confirming, setConfirming] = useState(false);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
@@ -123,29 +126,36 @@ export function PlantDetail({ plant, careLogs, careSchedules, calendars }: Props
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 shrink-0">
|
||||
<ShareButton entityType="garden.plant" entityId={plant.id} />
|
||||
<Link href={`/garden/plants/${plant.id}/edit`} className="btn btn-ghost btn-sm">
|
||||
Edit
|
||||
</Link>
|
||||
{confirming ? (
|
||||
<div className="flex gap-1">
|
||||
<button className="btn btn-danger btn-sm" onClick={handleDelete} disabled={isPending}>
|
||||
Confirm
|
||||
<div className="flex flex-col items-end gap-2 shrink-0">
|
||||
<div className="flex gap-2">
|
||||
<ShareButton entityType="garden.plant" entityId={plant.id} />
|
||||
<Link href={`/garden/plants/${plant.id}/edit`} className="btn btn-ghost btn-sm">
|
||||
Edit
|
||||
</Link>
|
||||
{confirming ? (
|
||||
<div className="flex gap-1">
|
||||
<button
|
||||
className="btn btn-danger btn-sm"
|
||||
onClick={handleDelete}
|
||||
disabled={isPending}
|
||||
>
|
||||
Confirm
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-ghost btn-sm"
|
||||
onClick={() => setConfirming(false)}
|
||||
disabled={isPending}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<button className="btn btn-ghost btn-sm" onClick={() => setConfirming(true)}>
|
||||
Delete
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-ghost btn-sm"
|
||||
onClick={() => setConfirming(false)}
|
||||
disabled={isPending}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<button className="btn btn-ghost btn-sm" onClick={() => setConfirming(true)}>
|
||||
Delete
|
||||
</button>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
<ShareLinkList links={shareLinks} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user