Add extras and options CRUD, add sidebar, add kanban board, udpate packages
This commit is contained in:
44
src/lib/commissions/kanban.ts
Normal file
44
src/lib/commissions/kanban.ts
Normal file
@ -0,0 +1,44 @@
|
||||
export const COMMISSION_STATUSES = [
|
||||
"NEW",
|
||||
"REVIEWING",
|
||||
"ACCEPTED",
|
||||
"REJECTED",
|
||||
"INPROGRESS",
|
||||
"COMPLETED",
|
||||
"SPAM",
|
||||
] as const;
|
||||
|
||||
export type CommissionStatus = (typeof COMMISSION_STATUSES)[number];
|
||||
|
||||
export const BOARD_COLUMNS = {
|
||||
intake: {
|
||||
title: "Intake",
|
||||
statuses: ["NEW", "REVIEWING", "ACCEPTED"] as const,
|
||||
// when you drop into this column, we normalize to one canonical status:
|
||||
// NEW should usually be system-created; for manual moves, REVIEWING is safer.
|
||||
canonicalStatus: "REVIEWING" as const,
|
||||
},
|
||||
inProgress: {
|
||||
title: "In Progress",
|
||||
statuses: ["INPROGRESS"] as const,
|
||||
canonicalStatus: "INPROGRESS" as const,
|
||||
},
|
||||
completed: {
|
||||
title: "Completed",
|
||||
statuses: ["COMPLETED"] as const,
|
||||
canonicalStatus: "COMPLETED" as const,
|
||||
},
|
||||
} as const;
|
||||
|
||||
export type BoardColumnId = keyof typeof BOARD_COLUMNS;
|
||||
|
||||
export function columnIdForStatus(status: string): BoardColumnId | null {
|
||||
if (BOARD_COLUMNS.intake.statuses.includes(status as any)) return "intake";
|
||||
if (BOARD_COLUMNS.inProgress.statuses.includes(status as any)) return "inProgress";
|
||||
if (BOARD_COLUMNS.completed.statuses.includes(status as any)) return "completed";
|
||||
return null;
|
||||
}
|
||||
|
||||
export function canonicalStatusForColumn(col: BoardColumnId): CommissionStatus {
|
||||
return BOARD_COLUMNS[col].canonicalStatus as CommissionStatus;
|
||||
}
|
||||
62
src/lib/compose-refs.ts
Normal file
62
src/lib/compose-refs.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import * as React from "react";
|
||||
|
||||
type PossibleRef<T> = React.Ref<T> | undefined;
|
||||
|
||||
/**
|
||||
* Set a given ref to a given value
|
||||
* This utility takes care of different types of refs: callback refs and RefObject(s)
|
||||
*/
|
||||
function setRef<T>(ref: PossibleRef<T>, value: T) {
|
||||
if (typeof ref === "function") {
|
||||
return ref(value);
|
||||
}
|
||||
|
||||
if (ref !== null && ref !== undefined) {
|
||||
ref.current = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility to compose multiple refs together
|
||||
* Accepts callback refs and RefObject(s)
|
||||
*/
|
||||
function composeRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {
|
||||
return (node) => {
|
||||
let hasCleanup = false;
|
||||
const cleanups = refs.map((ref) => {
|
||||
const cleanup = setRef(ref, node);
|
||||
if (!hasCleanup && typeof cleanup === "function") {
|
||||
hasCleanup = true;
|
||||
}
|
||||
return cleanup;
|
||||
});
|
||||
|
||||
// React <19 will log an error to the console if a callback ref returns a
|
||||
// value. We don't use ref cleanups internally so this will only happen if a
|
||||
// user's ref callback returns a value, which we only expect if they are
|
||||
// using the cleanup functionality added in React 19.
|
||||
if (hasCleanup) {
|
||||
return () => {
|
||||
for (let i = 0; i < cleanups.length; i++) {
|
||||
const cleanup = cleanups[i];
|
||||
if (typeof cleanup === "function") {
|
||||
cleanup();
|
||||
} else {
|
||||
setRef(refs[i], null);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom hook that composes multiple refs
|
||||
* Accepts callback refs and RefObject(s)
|
||||
*/
|
||||
function useComposedRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: we want to memoize by all values
|
||||
return React.useCallback(composeRefs(...refs), refs);
|
||||
}
|
||||
|
||||
export { composeRefs, useComposedRefs };
|
||||
Reference in New Issue
Block a user