Skip to content

Commit

Permalink
feat: Integrate SurveyJS (#265)
Browse files Browse the repository at this point in the history
  • Loading branch information
evadecker authored Dec 7, 2024
1 parent f0e6afe commit bdc6e19
Show file tree
Hide file tree
Showing 35 changed files with 706 additions and 586 deletions.
5 changes: 5 additions & 0 deletions .changeset/nine-hotels-rest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"namesake": minor
---

Add end-to-end form creation and form filling with SurveyJS
10 changes: 4 additions & 6 deletions convex/_generated/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,15 @@ import type {
} from "convex/server";
import type * as auth from "../auth.js";
import type * as constants from "../constants.js";
import type * as forms from "../forms.js";
import type * as documents from "../documents.js";
import type * as helpers from "../helpers.js";
import type * as http from "../http.js";
import type * as passwordReset from "../passwordReset.js";
import type * as questFields from "../questFields.js";
import type * as questions from "../questions.js";
import type * as quests from "../quests.js";
import type * as seed from "../seed.js";
import type * as topics from "../topics.js";
import type * as userEncryptedData from "../userEncryptedData.js";
import type * as userFormData from "../userFormData.js";
import type * as userQuests from "../userQuests.js";
import type * as userSettings from "../userSettings.js";
import type * as users from "../users.js";
Expand All @@ -41,16 +40,15 @@ import type * as validators from "../validators.js";
declare const fullApi: ApiFromModules<{
auth: typeof auth;
constants: typeof constants;
forms: typeof forms;
documents: typeof documents;
helpers: typeof helpers;
http: typeof http;
passwordReset: typeof passwordReset;
questFields: typeof questFields;
questions: typeof questions;
quests: typeof quests;
seed: typeof seed;
topics: typeof topics;
userEncryptedData: typeof userEncryptedData;
userFormData: typeof userFormData;
userQuests: typeof userQuests;
userSettings: typeof userSettings;
users: typeof users;
Expand Down
52 changes: 0 additions & 52 deletions convex/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
AtSign,
Calendar,
CalendarClock,
CalendarDays,
Expand All @@ -13,26 +12,20 @@ import {
Computer,
Gamepad2,
GraduationCap,
Hash,
HeartPulse,
History,
House,
Landmark,
LaptopMinimal,
LetterText,
ListChecks,
LoaderCircle,
type LucideIcon,
Mail,
MapPin,
MessageCircle,
Milestone,
Moon,
Phone,
RectangleEllipsis,
Scale,
ShoppingBag,
SquareCheck,
Sun,
Zap,
} from "lucide-react";
Expand Down Expand Up @@ -101,51 +94,6 @@ interface FieldDetails {
icon: LucideIcon;
}

export type Field =
| "text"
| "textarea"
| "date"
| "select"
| "checkbox"
| "number"
| "email"
| "phone";

export const FIELDS: Record<string, FieldDetails> = {
text: {
label: "Text",
icon: RectangleEllipsis,
},
textarea: {
label: "Textarea",
icon: LetterText,
},
date: {
label: "Date",
icon: Calendar,
},
select: {
label: "Select",
icon: ListChecks,
},
checkbox: {
label: "Checkbox",
icon: SquareCheck,
},
number: {
label: "Number",
icon: Hash,
},
email: {
label: "Email",
icon: AtSign,
},
phone: {
label: "Phone",
icon: Phone,
},
} as const;

export type Theme = "system" | "light" | "dark";
export const THEMES: Record<Theme, FieldDetails> = {
system: {
Expand Down
52 changes: 26 additions & 26 deletions convex/forms.ts → convex/documents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,33 @@ import { jurisdiction } from "./validators";
export const getAll = query({
args: {},
handler: async (ctx) => {
return await ctx.db.query("forms").collect();
return await ctx.db.query("documents").collect();
},
});

export const getById = query({
args: { formId: v.id("forms") },
args: { documentId: v.id("documents") },
handler: async (ctx, args) => {
return await ctx.db.get(args.formId);
return await ctx.db.get(args.documentId);
},
});

export const getAllActive = query({
args: {},
handler: async (ctx) => {
return await ctx.db
.query("forms")
.query("documents")
.filter((q) => q.eq(q.field("deletionTime"), undefined))
.collect();
},
});

export const getURL = query({
args: { formId: v.id("forms") },
args: { documentId: v.id("documents") },
handler: async (ctx, args) => {
const form = await ctx.db.get(args.formId);
if (!form || !form.file) return null;
return await ctx.storage.getUrl(form.file);
const document = await ctx.db.get(args.documentId);
if (!document || !document.file) return null;
return await ctx.storage.getUrl(document.file);
},
});

Expand All @@ -52,9 +52,9 @@ export const generateUploadUrl = mutation(async (ctx) => {
});

export const upload = userMutation({
args: { formId: v.id("forms"), storageId: v.id("_storage") },
args: { documentId: v.id("documents"), storageId: v.id("_storage") },
handler: async (ctx, args) => {
return await ctx.db.patch(args.formId, {
return await ctx.db.patch(args.documentId, {
file: args.storageId,
});
},
Expand All @@ -63,16 +63,16 @@ export const upload = userMutation({
export const getByQuestId = query({
args: { questId: v.id("quests") },
handler: async (ctx, args) => {
const forms = await ctx.db
.query("forms")
const documents = await ctx.db
.query("documents")
.withIndex("quest", (q) => q.eq("questId", args.questId))
.filter((q) => q.eq(q.field("deletionTime"), undefined))
.collect();

return await Promise.all(
forms.map(async (form) => ({
...form,
url: form.file ? await ctx.storage.getUrl(form.file) : null,
documents.map(async (document) => ({
...document,
url: document.file ? await ctx.storage.getUrl(document.file) : null,
})),
);
},
Expand All @@ -81,15 +81,15 @@ export const getByQuestId = query({
export const create = userMutation({
args: {
title: v.string(),
formCode: v.optional(v.string()),
code: v.optional(v.string()),
jurisdiction: jurisdiction,
file: v.optional(v.id("_storage")),
questId: v.id("quests"),
},
handler: async (ctx, args) => {
return await ctx.db.insert("forms", {
return await ctx.db.insert("documents", {
title: args.title,
formCode: args.formCode,
code: args.code,
jurisdiction: args.jurisdiction,
file: args.file,
questId: args.questId,
Expand All @@ -99,25 +99,25 @@ export const create = userMutation({
});

export const softDelete = userMutation({
args: { formId: v.id("forms") },
args: { documentId: v.id("documents") },
handler: async (ctx, args) => {
await ctx.db.patch(args.formId, { deletionTime: Date.now() });
await ctx.db.patch(args.documentId, { deletionTime: Date.now() });
},
});

export const undoSoftDelete = userMutation({
args: { formId: v.id("forms") },
args: { documentId: v.id("documents") },
handler: async (ctx, args) => {
await ctx.db.patch(args.formId, { deletionTime: undefined });
await ctx.db.patch(args.documentId, { deletionTime: undefined });
},
});

export const deleteForever = userMutation({
args: { formId: v.id("forms") },
args: { documentId: v.id("documents") },
handler: async (ctx, args) => {
// TODO: Delete form references in other tables
// TODO: Delete document references in other tables

// Delete the form
await ctx.db.delete(args.formId);
// Delete the document
await ctx.db.delete(args.documentId);
},
});
41 changes: 0 additions & 41 deletions convex/questFields.ts

This file was deleted.

13 changes: 0 additions & 13 deletions convex/quests.test.ts

This file was deleted.

24 changes: 24 additions & 0 deletions convex/quests.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { v } from "convex/values";
import { Model } from "survey-core";
import { query } from "./_generated/server";
import { DEFAULT_TIME_REQUIRED } from "./constants";
import { userMutation } from "./helpers";
Expand Down Expand Up @@ -144,6 +145,29 @@ export const setContent = userMutation({
},
});

export const setFormSchema = userMutation({
args: {
questId: v.id("quests"),
saveNo: v.number(),
formSchema: v.optional(v.string()),
},
handler: async (ctx, args) => {
if (!args.formSchema) return;
try {
const submittedSchema = JSON.parse(args.formSchema);
const survey = new Model(submittedSchema);
const validatedSchema = survey.toJSON();

console.log("Validated schema:", JSON.stringify(validatedSchema));
await ctx.db.patch(args.questId, {
formSchema: JSON.stringify(validatedSchema),
});
} catch (e) {
console.error(e);
}
},
});

export const softDelete = userMutation({
args: { questId: v.id("quests") },
handler: async (ctx, args) => {
Expand Down
Loading

0 comments on commit bdc6e19

Please sign in to comment.