refactor: remove dead code identified by knip
Delete unused Fab component (superseded by QuickAddFab), unused server actions and queries (listDashboards, reorderDashboards, deleteCareLog, canAccessContainer, getOverduePlants, getCareDueSoon, addItemToDefaultList, reorderItems, getOrCreateDefaultList), unused helpers (getCurrentUser, BrandWordmark, TopbarSpacer, DEV_LOGIN_COOKIE, no-arg ensureDefault* wrappers, addListItem re-export), and drop export keyword from five internal-only functions. Verified with typecheck, eslint, and production build after each removal.
This commit is contained in:
@@ -19,22 +19,6 @@ export type DashboardMeta = {
|
||||
position: number;
|
||||
};
|
||||
|
||||
export async function listDashboards(): Promise<DashboardMeta[]> {
|
||||
const { user } = await getCurrentSession();
|
||||
const rows = await db
|
||||
.select({
|
||||
id: dashboards.id,
|
||||
name: dashboards.name,
|
||||
slug: dashboards.slug,
|
||||
isDefault: dashboards.isDefault,
|
||||
position: dashboards.position,
|
||||
})
|
||||
.from(dashboards)
|
||||
.where(eq(dashboards.userId, user.id))
|
||||
.orderBy(asc(dashboards.position), asc(dashboards.createdAt));
|
||||
return rows;
|
||||
}
|
||||
|
||||
export async function getDefaultDashboardSlug(): Promise<string> {
|
||||
const { user } = await getCurrentSession();
|
||||
const rows = await db
|
||||
@@ -160,19 +144,6 @@ export async function setDefaultDashboard(id: string): Promise<void> {
|
||||
revalidatePath("/");
|
||||
}
|
||||
|
||||
export async function reorderDashboards(orderedIds: string[]): Promise<void> {
|
||||
const { user } = await getCurrentSession();
|
||||
await Promise.all(
|
||||
orderedIds.map((id, i) =>
|
||||
db
|
||||
.update(dashboards)
|
||||
.set({ position: i })
|
||||
.where(and(eq(dashboards.id, id), eq(dashboards.userId, user.id))),
|
||||
),
|
||||
);
|
||||
revalidatePath("/");
|
||||
}
|
||||
|
||||
export async function saveDashboardLayout(id: string, layout: DashboardLayout): Promise<void> {
|
||||
const { user } = await getCurrentSession();
|
||||
// Validate each widget's config against its registered schema
|
||||
|
||||
@@ -21,12 +21,3 @@ export function BrandMark({ size = "md", className }: { size?: "sm" | "md"; clas
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export function BrandWordmark({ className }: { className?: string }) {
|
||||
return (
|
||||
<span className={cn("brand", className)}>
|
||||
<BrandMark />
|
||||
<span className="brand-name">famapp</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useQuickAdd } from "@/components/quick-add-provider";
|
||||
import { NavIcon } from "@/components/nav-icon";
|
||||
|
||||
export function Fab() {
|
||||
const { openSheet } = useQuickAdd();
|
||||
|
||||
return (
|
||||
<button type="button" className="fab" aria-label="Quick add" onClick={openSheet}>
|
||||
<NavIcon name="plus" className="size-6" strokeWidth={2.4} />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -17,7 +17,7 @@ const SECTIONS = [
|
||||
|
||||
export type SectionId = (typeof SECTIONS)[number]["id"];
|
||||
|
||||
export function SettingsSidebar({ active }: { active: SectionId }) {
|
||||
function SettingsSidebar({ active }: { active: SectionId }) {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
|
||||
@@ -78,7 +78,7 @@ function SectionHashSync({ pathname }: { pathname: string | null }) {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function SettingsTabsMobile({ active }: { active: SectionId }) {
|
||||
function SettingsTabsMobile({ active }: { active: SectionId }) {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<div className="grid grid-cols-2 gap-1.5 mb-1">
|
||||
|
||||
@@ -6,7 +6,6 @@ import { NotificationBell } from "@/components/notification-bell";
|
||||
import { TopbarSearch } from "@/components/topbar-search";
|
||||
import { TopbarTitle } from "@/components/topbar-title";
|
||||
import { TopbarNewButton } from "@/components/topbar-new-button";
|
||||
import { NavIcon } from "@/components/nav-icon";
|
||||
|
||||
async function getNotifications(userId: string) {
|
||||
const rows = await db
|
||||
@@ -78,14 +77,3 @@ export async function Topbar() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function TopbarSpacer() {
|
||||
return (
|
||||
<div className="topbar">
|
||||
<h1 className="serif">famapp</h1>
|
||||
<div className="topbar-actions">
|
||||
<NavIcon name="bell" className="size-4 text-[var(--ink-mute)]" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -49,12 +49,3 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export async function getCurrentUser() {
|
||||
const session = await auth();
|
||||
if (!session?.user?.id) return null;
|
||||
const user = await db.query.users.findFirst({
|
||||
where: (u, { eq }) => eq(u.id, session.user.id),
|
||||
});
|
||||
return user ?? null;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { computePresetLayoutFromMetas } from "./dashboard";
|
||||
/** Build a layout matching one of the design's three dashboard arrangements
|
||||
* using the live module registry. Server-only — the registry is empty on
|
||||
* the client. */
|
||||
export function computePresetLayout(preset: PresetId): DashboardLayout {
|
||||
function computePresetLayout(preset: PresetId): DashboardLayout {
|
||||
const { widgets } = getRegistry();
|
||||
if (widgets.length === 0) return { version: 1, widgets: [] };
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
export const DEV_LOGIN_COOKIE = "authjs.session-token";
|
||||
|
||||
if (
|
||||
process.env.NODE_ENV === "production" &&
|
||||
process.env.ENABLE_DEV_LOGIN === "true" &&
|
||||
|
||||
@@ -54,7 +54,7 @@ export async function listReminders(entityType: string, entityId: string) {
|
||||
.where(and(eq(reminders.entityType, entityType), eq(reminders.entityId, entityId)));
|
||||
}
|
||||
|
||||
export async function tickReminders() {
|
||||
async function tickReminders() {
|
||||
let dueReminders: (typeof reminders.$inferSelect)[] = [];
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { db } from "@/lib/db";
|
||||
import { householdMembers, households, users } from "@/modules/_core/schema";
|
||||
import { householdMembers } from "@/modules/_core/schema";
|
||||
import { calendars } from "../schema";
|
||||
|
||||
const HOME_COLOR = "#2563eb";
|
||||
@@ -17,22 +17,6 @@ export async function ensureDefaultCalendarsForMembership({
|
||||
await ensurePersonalCalendar({ householdId, userId });
|
||||
}
|
||||
|
||||
export async function ensureDefaultCalendars() {
|
||||
const householdRows = await db.select().from(households);
|
||||
for (const household of householdRows) {
|
||||
await ensureHomeCalendar(household.id);
|
||||
}
|
||||
|
||||
const memberships = await db
|
||||
.select({ householdId: householdMembers.householdId, userId: users.id })
|
||||
.from(householdMembers)
|
||||
.innerJoin(users, eq(householdMembers.userId, users.id));
|
||||
|
||||
for (const membership of memberships) {
|
||||
await ensurePersonalCalendar(membership);
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureHomeCalendar(householdId: string) {
|
||||
const [existing] = await db
|
||||
.select({ id: calendars.id })
|
||||
|
||||
@@ -451,22 +451,6 @@ export async function logCare(input: z.input<typeof careLogInput>) {
|
||||
return log;
|
||||
}
|
||||
|
||||
export async function deleteCareLog(input: { id: string }) {
|
||||
const parsed = z.object({ id: z.string().uuid() }).parse(input);
|
||||
const { household } = await getCurrentSession();
|
||||
|
||||
const [row] = await db
|
||||
.select({ id: gardenCareLogs.id, plantId: gardenCareLogs.plantId })
|
||||
.from(gardenCareLogs)
|
||||
.where(and(eq(gardenCareLogs.id, parsed.id), eq(gardenCareLogs.householdId, household.id)))
|
||||
.limit(1);
|
||||
|
||||
if (!row) throw new Error("Forbidden");
|
||||
|
||||
await db.delete(gardenCareLogs).where(eq(gardenCareLogs.id, parsed.id));
|
||||
revalidatePath(`/garden/plants/${row.plantId}`);
|
||||
}
|
||||
|
||||
// ─── Care schedule actions ────────────────────────────────────────────────────
|
||||
|
||||
const careScheduleInput = z.object({
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export { addItem as addListItem } from "@/modules/lists/server/actions";
|
||||
export { getList, listLists } from "@/modules/lists/server/queries";
|
||||
|
||||
import { addItem } from "@/modules/lists/server/actions";
|
||||
|
||||
@@ -110,16 +110,6 @@ export async function getContainer(id: string): Promise<ContainerDetailDto | nul
|
||||
};
|
||||
}
|
||||
|
||||
export async function canAccessContainer(id: string, householdId: string): Promise<boolean> {
|
||||
const [row] = await db
|
||||
.select({ id: gardenContainers.id })
|
||||
.from(gardenContainers)
|
||||
.where(and(eq(gardenContainers.id, id), eq(gardenContainers.householdId, householdId)))
|
||||
.limit(1);
|
||||
|
||||
return !!row;
|
||||
}
|
||||
|
||||
export async function searchContainers(query: string, householdId: string) {
|
||||
const rows = await db
|
||||
.select({ id: gardenContainers.id, name: gardenContainers.name })
|
||||
@@ -430,74 +420,6 @@ export async function getCareSchedules(plantId: string): Promise<CareScheduleDto
|
||||
});
|
||||
}
|
||||
|
||||
export type OverduePlantDto = {
|
||||
id: string;
|
||||
name: string;
|
||||
primaryImageUrl: string | null;
|
||||
mostOverdueAt: Date;
|
||||
};
|
||||
|
||||
export async function getOverduePlants(householdId: string): Promise<OverduePlantDto[]> {
|
||||
const rows = await db
|
||||
.select({
|
||||
id: gardenPlants.id,
|
||||
name: gardenPlants.name,
|
||||
primaryImageUrl: gardenPlants.primaryImageUrl,
|
||||
mostOverdueAt: sql<Date>`min(${gardenCareSchedules.nextDueAt})`,
|
||||
})
|
||||
.from(gardenPlants)
|
||||
.innerJoin(
|
||||
gardenCareSchedules,
|
||||
and(
|
||||
eq(gardenCareSchedules.plantId, gardenPlants.id),
|
||||
eq(gardenCareSchedules.enabled, true),
|
||||
lte(gardenCareSchedules.nextDueAt, sql`now()`),
|
||||
),
|
||||
)
|
||||
.where(eq(gardenPlants.householdId, householdId))
|
||||
.groupBy(gardenPlants.id, gardenPlants.name, gardenPlants.primaryImageUrl)
|
||||
.orderBy(sql`min(${gardenCareSchedules.nextDueAt})`);
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
export type CareDueSoonDto = {
|
||||
id: string;
|
||||
name: string;
|
||||
primaryImageUrl: string | null;
|
||||
nextDueAt: Date;
|
||||
};
|
||||
|
||||
export async function getCareDueSoon(
|
||||
householdId: string,
|
||||
withinDays: number,
|
||||
): Promise<CareDueSoonDto[]> {
|
||||
const cutoff = new Date();
|
||||
cutoff.setDate(cutoff.getDate() + withinDays);
|
||||
|
||||
const rows = await db
|
||||
.select({
|
||||
id: gardenPlants.id,
|
||||
name: gardenPlants.name,
|
||||
primaryImageUrl: gardenPlants.primaryImageUrl,
|
||||
nextDueAt: sql<Date>`min(${gardenCareSchedules.nextDueAt})`,
|
||||
})
|
||||
.from(gardenPlants)
|
||||
.innerJoin(
|
||||
gardenCareSchedules,
|
||||
and(
|
||||
eq(gardenCareSchedules.plantId, gardenPlants.id),
|
||||
eq(gardenCareSchedules.enabled, true),
|
||||
lte(gardenCareSchedules.nextDueAt, cutoff),
|
||||
),
|
||||
)
|
||||
.where(eq(gardenPlants.householdId, householdId))
|
||||
.groupBy(gardenPlants.id, gardenPlants.name, gardenPlants.primaryImageUrl)
|
||||
.orderBy(sql`min(${gardenCareSchedules.nextDueAt})`);
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
// ─── Widget queries ───────────────────────────────────────────────────────────
|
||||
|
||||
export type CareDueWidgetRow = {
|
||||
|
||||
@@ -102,7 +102,7 @@ export async function searchSpecies(query: string): Promise<SpeciesSuggestion[]>
|
||||
}
|
||||
}
|
||||
|
||||
export async function getSpeciesById(id: string): Promise<SpeciesSuggestion | null> {
|
||||
async function getSpeciesById(id: string): Promise<SpeciesSuggestion | null> {
|
||||
const cutoff = new Date(Date.now() - CACHE_TTL_MS);
|
||||
const [cached] = await db
|
||||
.select()
|
||||
|
||||
@@ -8,7 +8,6 @@ import { getCurrentSession } from "@/lib/session";
|
||||
import { logActivity } from "@/modules/_core/activity";
|
||||
import { fireItemToggleHooks } from "@/modules/_core/registry";
|
||||
import { listItems, lists } from "../schema";
|
||||
import { getOrCreateDefaultList } from "./defaults";
|
||||
import { canAccessList, getList } from "./queries";
|
||||
import { notifyListChanged } from "./realtime";
|
||||
|
||||
@@ -122,18 +121,6 @@ export async function addItem(input: z.input<typeof itemInput>) {
|
||||
return getList(parsed.listId);
|
||||
}
|
||||
|
||||
export async function addItemToDefaultList(input: { type: string; text: string }) {
|
||||
const parsed = z
|
||||
.object({
|
||||
type: listInput.shape.type,
|
||||
text: itemInput.shape.text,
|
||||
})
|
||||
.parse(input);
|
||||
const { household } = await getCurrentSession();
|
||||
const list = await getOrCreateDefaultList({ householdId: household.id, type: parsed.type });
|
||||
return addItem({ listId: list.id, text: parsed.text });
|
||||
}
|
||||
|
||||
export async function toggleItem(input: { id: string; done?: boolean }) {
|
||||
const parsed = z.object({ id: z.string().uuid(), done: z.boolean().optional() }).parse(input);
|
||||
const { household, user } = await getCurrentSession();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { db } from "@/lib/db";
|
||||
import { households } from "@/modules/_core/schema";
|
||||
import { lists } from "../schema";
|
||||
|
||||
const DEFAULT_LISTS = [
|
||||
@@ -14,26 +13,6 @@ export async function ensureDefaultListsForHousehold(householdId: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function ensureDefaultLists() {
|
||||
const householdRows = await db.select({ id: households.id }).from(households);
|
||||
for (const household of householdRows) {
|
||||
await ensureDefaultListsForHousehold(household.id);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getOrCreateDefaultList({
|
||||
householdId,
|
||||
type,
|
||||
}: {
|
||||
householdId: string;
|
||||
type: "shopping" | "task" | string;
|
||||
}) {
|
||||
const defaults = DEFAULT_LISTS.find((list) => list.type === type);
|
||||
const name = defaults?.name ?? type;
|
||||
|
||||
return ensureDefaultList({ householdId, type, name });
|
||||
}
|
||||
|
||||
async function ensureDefaultList({
|
||||
householdId,
|
||||
type,
|
||||
|
||||
Reference in New Issue
Block a user