fix: tighten sharing and shadcn composition
This commit is contained in:
@@ -17,6 +17,7 @@ import type { CalView } from "@/modules/_core/themes";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
@@ -48,6 +49,10 @@ type EventDraft = {
|
||||
type MobileView = "day" | "week" | "agenda";
|
||||
|
||||
const DEFAULT_COLOR = "#B85C3C";
|
||||
const VISIBILITY_ITEMS = [
|
||||
{ label: "Household", value: "household" },
|
||||
{ label: "Private", value: "private" },
|
||||
];
|
||||
|
||||
const VIEW_MAP: Record<CalView, string> = {
|
||||
month: "dayGridMonth",
|
||||
@@ -115,6 +120,10 @@ export function CalendarShell({
|
||||
() => eventRows.filter((event) => visibleIds.has(event.calendarId)),
|
||||
[eventRows, visibleIds],
|
||||
);
|
||||
const calendarSelectItems = useMemo(
|
||||
() => calendarRows.map((calendar) => ({ label: calendar.name, value: calendar.id })),
|
||||
[calendarRows],
|
||||
);
|
||||
|
||||
function toggleCalendar(id: string) {
|
||||
setVisibleIds((current) => {
|
||||
@@ -343,6 +352,7 @@ export function CalendarShell({
|
||||
onChange={(event) => updateCalendar(calendar, { color: event.target.value })}
|
||||
/>
|
||||
<Select
|
||||
items={VISIBILITY_ITEMS}
|
||||
value={calendar.visibility}
|
||||
onValueChange={(value) =>
|
||||
updateCalendar(calendar, { visibility: value as "private" | "household" })
|
||||
@@ -354,8 +364,13 @@ export function CalendarShell({
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="household">Household</SelectItem>
|
||||
<SelectItem value="private">Private</SelectItem>
|
||||
<SelectGroup>
|
||||
{VISIBILITY_ITEMS.map((item) => (
|
||||
<SelectItem key={item.value} value={item.value}>
|
||||
{item.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Button
|
||||
@@ -391,6 +406,7 @@ export function CalendarShell({
|
||||
onChange={(event) => setCalendarColorValue(event.target.value)}
|
||||
/>
|
||||
<Select
|
||||
items={VISIBILITY_ITEMS}
|
||||
value={calendarVisibility}
|
||||
onValueChange={(value) =>
|
||||
setCalendarVisibilityValue(value as "private" | "household")
|
||||
@@ -402,8 +418,13 @@ export function CalendarShell({
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="household">Household</SelectItem>
|
||||
<SelectItem value="private">Private</SelectItem>
|
||||
<SelectGroup>
|
||||
{VISIBILITY_ITEMS.map((item) => (
|
||||
<SelectItem key={item.value} value={item.value}>
|
||||
{item.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
@@ -444,6 +465,7 @@ export function CalendarShell({
|
||||
/>
|
||||
<Label htmlFor="event-calendar">Calendar</Label>
|
||||
<Select
|
||||
items={calendarSelectItems}
|
||||
value={selectedEvent.calendarId}
|
||||
onValueChange={(value) => setSelectedEvent({ ...selectedEvent, calendarId: value ?? "" })}
|
||||
>
|
||||
@@ -454,11 +476,13 @@ export function CalendarShell({
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{calendarRows.map((calendar) => (
|
||||
<SelectItem key={calendar.id} value={calendar.id}>
|
||||
{calendar.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
<SelectGroup>
|
||||
{calendarSelectItems.map((calendar) => (
|
||||
<SelectItem key={calendar.value} value={calendar.value}>
|
||||
{calendar.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
|
||||
@@ -2,6 +2,8 @@ import type { ModuleManifest, WidgetContext } from "../_core/module";
|
||||
import { z } from "zod";
|
||||
import { listCalendars, listEvents, searchCalendars, searchEvents } from "./server/queries";
|
||||
import {
|
||||
canShareCalendar,
|
||||
canShareEvent,
|
||||
loadCalendarForShare,
|
||||
loadEventForShare,
|
||||
type CalendarShareData,
|
||||
@@ -150,7 +152,8 @@ const manifest: ModuleManifest = {
|
||||
share: { canShare: true, defaultCapabilities: ["read"] },
|
||||
search: { search: searchCalendars },
|
||||
resolveUrl: (id) => `/calendar?id=${id}`,
|
||||
loadForShare: (id) => loadCalendarForShare(id),
|
||||
canShareEntity: canShareCalendar,
|
||||
loadForShare: loadCalendarForShare,
|
||||
renderSharedView: ({ data }) => <CalendarSharedView data={data as CalendarShareData} />,
|
||||
renderActivity: (entry) => {
|
||||
const name = entry.payload?.name as string | undefined;
|
||||
@@ -166,7 +169,8 @@ const manifest: ModuleManifest = {
|
||||
reminder: { canRemind: true },
|
||||
search: { search: searchEvents },
|
||||
resolveUrl: (id) => `/calendar/events/${id}`,
|
||||
loadForShare: (id) => loadEventForShare(id),
|
||||
canShareEntity: canShareEvent,
|
||||
loadForShare: loadEventForShare,
|
||||
renderSharedView: ({ data }) => <EventSharedView data={data as EventShareData} />,
|
||||
renderActivity: (entry) => {
|
||||
const title = entry.payload?.title as string | undefined;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { and, asc, eq, gte, lte } from "drizzle-orm";
|
||||
import { db } from "@/lib/db";
|
||||
import type { PublicShareContext, ShareContext } from "@/modules/_core/module";
|
||||
import { calendarEvents, calendars } from "../schema";
|
||||
|
||||
export type EventSummary = {
|
||||
@@ -21,11 +22,51 @@ export type CalendarShareData = {
|
||||
|
||||
export type EventShareData = EventSummary & { calendarName: string };
|
||||
|
||||
export async function loadCalendarForShare(id: string): Promise<CalendarShareData | null> {
|
||||
function canSeeCalendarRow(
|
||||
row: { householdId: string; ownerId: string; visibility: string },
|
||||
ctx: ShareContext,
|
||||
): boolean {
|
||||
if (row.householdId !== ctx.householdId) return false;
|
||||
return row.visibility === "household" || row.ownerId === ctx.userId;
|
||||
}
|
||||
|
||||
export async function canShareCalendar(id: string, ctx: ShareContext): Promise<boolean> {
|
||||
const [calendar] = await db
|
||||
.select({
|
||||
householdId: calendars.householdId,
|
||||
ownerId: calendars.ownerId,
|
||||
visibility: calendars.visibility,
|
||||
})
|
||||
.from(calendars)
|
||||
.where(eq(calendars.id, id))
|
||||
.limit(1);
|
||||
|
||||
return calendar ? canSeeCalendarRow(calendar, ctx) : false;
|
||||
}
|
||||
|
||||
export async function canShareEvent(id: string, ctx: ShareContext): Promise<boolean> {
|
||||
const [row] = await db
|
||||
.select({
|
||||
householdId: calendars.householdId,
|
||||
ownerId: calendars.ownerId,
|
||||
visibility: calendars.visibility,
|
||||
})
|
||||
.from(calendarEvents)
|
||||
.innerJoin(calendars, eq(calendarEvents.calendarId, calendars.id))
|
||||
.where(eq(calendarEvents.id, id))
|
||||
.limit(1);
|
||||
|
||||
return row ? canSeeCalendarRow(row, ctx) : false;
|
||||
}
|
||||
|
||||
export async function loadCalendarForShare(
|
||||
id: string,
|
||||
ctx: PublicShareContext,
|
||||
): Promise<CalendarShareData | null> {
|
||||
const [calendar] = await db
|
||||
.select({ id: calendars.id, name: calendars.name, color: calendars.color })
|
||||
.from(calendars)
|
||||
.where(eq(calendars.id, id))
|
||||
.where(and(eq(calendars.id, id), eq(calendars.householdId, ctx.householdId)))
|
||||
.limit(1);
|
||||
|
||||
if (!calendar) return null;
|
||||
@@ -63,7 +104,10 @@ export async function loadCalendarForShare(id: string): Promise<CalendarShareDat
|
||||
};
|
||||
}
|
||||
|
||||
export async function loadEventForShare(id: string): Promise<EventShareData | null> {
|
||||
export async function loadEventForShare(
|
||||
id: string,
|
||||
ctx: PublicShareContext,
|
||||
): Promise<EventShareData | null> {
|
||||
const [row] = await db
|
||||
.select({
|
||||
id: calendarEvents.id,
|
||||
@@ -77,7 +121,7 @@ export async function loadEventForShare(id: string): Promise<EventShareData | nu
|
||||
})
|
||||
.from(calendarEvents)
|
||||
.innerJoin(calendars, eq(calendarEvents.calendarId, calendars.id))
|
||||
.where(eq(calendarEvents.id, id))
|
||||
.where(and(eq(calendarEvents.id, id), eq(calendars.householdId, ctx.householdId)))
|
||||
.limit(1);
|
||||
|
||||
if (!row) return null;
|
||||
|
||||
Reference in New Issue
Block a user