- {row.original.schemaVersion}
+ cell: ({
+ row,
+ table: {
+ options: { meta },
+ },
+ }) => (
+
+ {new Intl.DateTimeFormat(meta.navigatorLanguages, dateOptions).format(
+ new Date(row.original.lastModified),
+ )}
),
},
- {
- accessorKey: 'active',
- header: 'Active',
- cell: ({ row }) => {
- return (
-
- );
- },
- },
];
diff --git a/app/(dashboard)/dashboard/_components/ProtocolsTable/ProtocolsTable.tsx b/app/(dashboard)/dashboard/_components/ProtocolsTable/ProtocolsTable.tsx
index 3ddfecc5a..e21b87528 100644
--- a/app/(dashboard)/dashboard/_components/ProtocolsTable/ProtocolsTable.tsx
+++ b/app/(dashboard)/dashboard/_components/ProtocolsTable/ProtocolsTable.tsx
@@ -37,7 +37,7 @@ export const ProtocolsTable = ({
<>
{isLoading &&
Loading...
}
= TTable & {
+ options?: {
+ meta?: {
+ getRowClasses?: (row: Row) => string | undefined;
+ navigatorLanguages?: string[];
+ };
+ };
+};
+
interface DataTableProps {
columns?: ColumnDef[];
data: TData[];
@@ -44,6 +55,15 @@ export function DataTable({
const [sorting, setSorting] = useState([]);
const [isDeleting, setIsDeleting] = useState(false);
const [rowSelection, setRowSelection] = useState({});
+ const [navigatorLanguages, setNavigatorLanguages] = useState<
+ string[] | undefined
+ >();
+
+ useEffect(() => {
+ if (window.navigator.languages) {
+ setNavigatorLanguages(window.navigator.languages as string[]);
+ }
+ }, []);
const [columnFilters, setColumnFilters] = useState([]);
@@ -92,12 +112,17 @@ export function DataTable({
onRowSelectionChange: setRowSelection,
onColumnFiltersChange: setColumnFilters,
getFilteredRowModel: getFilteredRowModel(),
+ meta: {
+ getRowClasses: (row) =>
+ row.original.active && 'bg-purple-500/30 hover:bg-purple-500/40',
+ navigatorLanguages,
+ },
state: {
sorting,
rowSelection,
columnFilters,
},
- });
+ }) as CustomTable;
const hasSelectedRows = table.getSelectedRowModel().rows.length > 0;
@@ -147,6 +172,7 @@ export function DataTable({
{row.getVisibleCells().map((cell) => (
diff --git a/components/DataTable/DefaultColumns.tsx b/components/DataTable/DefaultColumns.tsx
index 8e71296a9..2d8072768 100644
--- a/components/DataTable/DefaultColumns.tsx
+++ b/components/DataTable/DefaultColumns.tsx
@@ -1,8 +1,4 @@
-import { type ColumnDef } from '@tanstack/react-table';
-
-export const makeDefaultColumns = (
- data: TData[],
-): ColumnDef[] => {
+export const makeDefaultColumns = (data: TData[]) => {
const firstRow = data[0];
if (!firstRow || typeof firstRow !== 'object') {
@@ -11,7 +7,7 @@ export const makeDefaultColumns = (
const columnKeys = Object.keys(firstRow);
- const columns: ColumnDef[] = columnKeys.map((key) => {
+ const columns = columnKeys.map((key) => {
return {
accessorKey: key,
header: key,
diff --git a/components/DataTable/helpers.ts b/components/DataTable/helpers.ts
new file mode 100644
index 000000000..7a65e8cc7
--- /dev/null
+++ b/components/DataTable/helpers.ts
@@ -0,0 +1,8 @@
+// Display options for dates: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat#using_options
+export const dateOptions: Intl.DateTimeFormatOptions = {
+ year: 'numeric',
+ month: 'numeric',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: 'numeric',
+};
diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx
new file mode 100644
index 000000000..ccc9cd787
--- /dev/null
+++ b/components/ui/badge.tsx
@@ -0,0 +1,36 @@
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "~/utils/shadcn"
+
+const badgeVariants = cva(
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
+ {
+ variants: {
+ variant: {
+ default:
+ "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
+ secondary:
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ destructive:
+ "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
+ outline: "text-foreground",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+export interface BadgeProps
+ extends React.HTMLAttributes,
+ VariantProps {}
+
+function Badge({ className, variant, ...props }: BadgeProps) {
+ return (
+
+ )
+}
+
+export { Badge, badgeVariants }
diff --git a/declarations.d.ts b/declarations.d.ts
index 8b1378917..e69de29bb 100644
--- a/declarations.d.ts
+++ b/declarations.d.ts
@@ -1 +0,0 @@
-
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index ca19a1792..54f3b7af9 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -14,6 +14,7 @@ datasource db {
model Protocol {
id String @id @default(cuid())
+ active Boolean @default(false)
hash String @unique
name String
schemaVersion Int
@@ -24,7 +25,6 @@ model Protocol {
codebook Json
assets Asset[]
interviews Interview[]
- active Boolean @default(false)
}
model Asset {
diff --git a/server/routers/protocol.ts b/server/routers/protocol.ts
index f7cba90c4..ecf77c28a 100644
--- a/server/routers/protocol.ts
+++ b/server/routers/protocol.ts
@@ -7,11 +7,6 @@ import { Prisma } from '@prisma/client';
import { utapi } from '~/app/api/uploadthing/core';
import type { Protocol } from '@codaco/shared-consts';
-const updateActiveProtocolSchema = z.object({
- input: z.boolean(),
- hash: z.string(),
-});
-
export const assetInsertSchema = z.array(
z.object({
key: z.string(),
@@ -114,64 +109,41 @@ export const protocolRouter = router({
active: true,
},
});
- return protocol;
- }),
- is: protectedProcedure.input(z.string()).query(async ({ input: hash }) => {
- const protocol = await prisma.protocol.findFirst({
- where: {
- hash,
- },
- select: {
- active: true,
- },
- });
- return protocol?.active || false;
+ return protocol?.id ?? null;
}),
+ is: protectedProcedure
+ .input(z.string())
+ .mutation(async ({ input: protocolId }) => {
+ const protocol = await prisma.protocol.findFirst({
+ where: {
+ id: protocolId,
+ },
+ });
+
+ return protocol?.active ?? false;
+ }),
set: protectedProcedure
- .input(updateActiveProtocolSchema)
- .mutation(async ({ input: { input, hash } }) => {
+ .input(z.string())
+ .mutation(async ({ input: protocolId }) => {
try {
- const currentActive = await prisma.protocol.findFirst({
- where: {
- active: true,
- },
- });
-
- // If input is false, deactivate the active protocol
- if (!input) {
- await prisma.protocol.update({
+ await prisma.$transaction([
+ prisma.protocol.updateMany({
where: {
- hash: hash,
active: true,
},
data: {
active: false,
},
- });
- return { error: null, success: true };
- }
-
- // Deactivate the current active protocol, if it exists
- if (currentActive) {
- await prisma.protocol.update({
+ }),
+ prisma.protocol.update({
where: {
- id: currentActive.id,
+ id: protocolId,
},
data: {
- active: false,
+ active: true,
},
- });
- }
-
- // Make the protocol with the given hash active
- await prisma.protocol.update({
- where: {
- hash,
- },
- data: {
- active: true,
- },
- });
+ }),
+ ]);
return { error: null, success: true };
} catch (error) {