Skip to content

Commit

Permalink
folder wip
Browse files Browse the repository at this point in the history
  • Loading branch information
kubk committed Jan 6, 2024
1 parent 412d52a commit c40b085
Show file tree
Hide file tree
Showing 40 changed files with 894 additions and 243 deletions.
45 changes: 45 additions & 0 deletions functions/db/databaseTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export interface Database {
deck_id: number
duration_days: number | null
id: number
processed_at: string | null
share_id: string
usage_started_at: string | null
used_by: number | null
Expand All @@ -121,6 +122,7 @@ export interface Database {
deck_id: number
duration_days?: number | null
id?: number
processed_at?: string | null
share_id: string
usage_started_at?: string | null
used_by?: number | null
Expand All @@ -131,6 +133,7 @@ export interface Database {
deck_id?: number
duration_days?: number | null
id?: number
processed_at?: string | null
share_id?: string
usage_started_at?: string | null
used_by?: number | null
Expand Down Expand Up @@ -249,18 +252,21 @@ export interface Database {
Row: {
author_id: number
created_at: string
description: string | null
id: number
title: string
}
Insert: {
author_id: number
created_at?: string
description?: string | null
id?: number
title: string
}
Update: {
author_id?: number
created_at?: string
description?: string | null
id?: number
title?: string
}
Expand Down Expand Up @@ -370,6 +376,34 @@ export interface Database {
}
]
}
user_features: {
Row: {
advanced_share: boolean
created_at: string
id: number
user_id: number
}
Insert: {
advanced_share?: boolean
created_at?: string
id?: number
user_id: number
}
Update: {
advanced_share?: boolean
created_at?: string
id?: number
user_id?: number
}
Relationships: [
{
foreignKeyName: "user_features_user_id_fkey"
columns: ["user_id"]
referencedRelation: "user"
referencedColumns: ["id"]
}
]
}
user_folder: {
Row: {
created_at: string
Expand Down Expand Up @@ -435,6 +469,17 @@ export interface Database {
}[]
}
get_folder_with_decks: {
Args: {
usr_id: number
}
Returns: {
folder_id: number
folder_title: string
folder_description: string
deck_id: number
}[]
}
get_folder_with_decks_backup: {
Args: {
usr_id: number
}
Expand Down
27 changes: 0 additions & 27 deletions functions/db/deck/get-all-my-decks-flat.ts

This file was deleted.

23 changes: 23 additions & 0 deletions functions/db/folder/get-folder-by-id-and-author-id.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { EnvSafe } from "../../env/env-schema.ts";
import { getDatabase } from "../get-database.ts";
import { DatabaseException } from "../database-exception.ts";

export const getFolderByIdAndAuthorId = async (
envSafe: EnvSafe,
folderId: number,
user: { id: number; is_admin: boolean },
) => {
const db = getDatabase(envSafe);

let query = db.from("folder").select().eq("id", folderId);
if (!user.is_admin) {
query = query.eq("author_id", user.id);
}

const canEditResult = await query.single();
if (canEditResult.error) {
throw new DatabaseException(canEditResult.error);
}

return canEditResult.data ?? null;
};
1 change: 1 addition & 0 deletions functions/db/folder/get-folders-with-decks-db.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { z } from "zod";
const userFoldersSchema = z.object({
folder_id: z.number(),
folder_title: z.string(),
folder_description: z.string().nullable(),
deck_id: z.number().nullable(),
});

Expand Down
21 changes: 21 additions & 0 deletions functions/decks-mine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { handleError } from "./lib/handle-error/handle-error.ts";
import { getUser } from "./services/get-user.ts";
import { createAuthFailedResponse } from "./lib/json-response/create-auth-failed-response.ts";
import { envSchema } from "./env/env-schema.ts";
import { getDecksCreatedByMe } from "./db/deck/get-decks-created-by-me.ts";
import { DeckWithoutCardsDbType } from "./db/deck/decks-with-cards-schema.ts";
import { createJsonResponse } from "./lib/json-response/create-json-response.ts";

export type DecksMineResponse = {
decks: DeckWithoutCardsDbType[];
};

export const onRequest = handleError(async ({ request, env }) => {
const user = await getUser(request, env);
if (!user) return createAuthFailedResponse();
const envSafe = envSchema.parse(env);

const decks = await getDecksCreatedByMe(envSafe, user.id);

return createJsonResponse<DecksMineResponse>({ decks: decks });
});
63 changes: 56 additions & 7 deletions functions/upsert-folder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,23 @@ import { getDatabase } from "./db/get-database.ts";
import { envSchema } from "./env/env-schema.ts";
import { DatabaseException } from "./db/database-exception.ts";
import { createJsonResponse } from "./lib/json-response/create-json-response.ts";
import {
getFoldersWithDecksDb,
UserFoldersDbType,
} from "./db/folder/get-folders-with-decks-db.tsx";
import { getFolderByIdAndAuthorId } from "./db/folder/get-folder-by-id-and-author-id.ts";

const requestSchema = z.object({
id: z.number().optional(),
title: z.string(),
description: z.string().nullable(),
deckIds: z.array(z.number()),
});

export type AddFolderRequest = z.infer<typeof requestSchema>;
export type AddFolderResponse = null;
export type AddFolderResponse = {
folders: UserFoldersDbType[];
};

export const onRequestPost = handleError(async ({ request, env }) => {
const user = await getUser(request, env);
Expand All @@ -26,18 +35,58 @@ export const onRequestPost = handleError(async ({ request, env }) => {
}

const envSafe = envSchema.parse(env);

if (input.data.id) {
const canEdit = await getFolderByIdAndAuthorId(
envSafe,
input.data.id,
user,
);
if (!canEdit) {
return createBadRequestResponse();
}
}

const db = getDatabase(envSafe);
const { data } = input;

const upsertFolderResult = await db.from("folder").upsert({
id: data.id,
title: data.title,
author_id: user.id,
});
const upsertFolderResult = await db
.from("folder")
.upsert({
id: data.id,
title: data.title,
description: data.description,
author_id: user.id,
})
.select()
.single();

if (upsertFolderResult.error) {
throw new DatabaseException(upsertFolderResult.error);
}

return createJsonResponse<AddFolderResponse>(null);
const folderId = upsertFolderResult.data.id;

const oldDeckFolderResult = await db.from("deck_folder").delete().match({
folder_id: folderId,
});

if (oldDeckFolderResult.error) {
throw new DatabaseException(oldDeckFolderResult.error);
}

const upsertDeckFolderResult = await db.from("deck_folder").upsert(
data.deckIds.map((deckId) => ({
deck_id: deckId,
folder_id: folderId,
})),
);

if (upsertDeckFolderResult.error) {
throw new DatabaseException(upsertDeckFolderResult.error);
}

return createJsonResponse<AddFolderResponse>({
folders: await getFoldersWithDecksDb(envSafe, user.id),
});
});
5 changes: 5 additions & 0 deletions src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
AddDeckAccessRequest,
AddDeckAccessResponse,
} from "../../functions/add-deck-access.ts";
import { DecksMineResponse } from "../../functions/decks-mine.ts";

export const healthRequest = () => {
return request<HealthResponse>("/health");
Expand Down Expand Up @@ -128,3 +129,7 @@ export const apiFolderUpsert = (body: AddFolderRequest) => {
body,
);
};

export const apiDecksMine = () => {
return request<DecksMineResponse>("/decks-mine");
};
3 changes: 2 additions & 1 deletion src/lib/mobx-form/boolean-field.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { makeAutoObservable } from "mobx";
import { TouchableField } from "./touchable-field.ts";

export class BooleanField {
export class BooleanField implements TouchableField {
isTouched = false;

constructor(
Expand Down
6 changes: 6 additions & 0 deletions src/lib/mobx-form/field-with-value.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
export type FieldWithValue<T> = {
value: T;
};

export const isFieldWithValue = (
object: unknown,
): object is FieldWithValue<unknown> => {
return typeof object === "object" && object !== null && "value" in object;
};
45 changes: 45 additions & 0 deletions src/lib/mobx-form/form-has-error.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { expect, test } from "vitest";
import { TextField } from "./text-field.ts";
import {
formTouchAll,
formUnTouchAll,
isFormEmpty,
isFormTouched,
isFormTouchedAndValid,
isFormValid,
} from "./form-has-error.ts";
import { validators } from "./validator.ts";
import { BooleanField } from "./boolean-field.ts";
import { ListField } from "./list-field.ts";

const isRequiredMessage = "is required";

Expand Down Expand Up @@ -148,3 +151,45 @@ test("very nested form - any fields", () => {
expect(isFormTouched(f)).toBeTruthy();
expect(isFormValid(f)).toBeFalsy();
});

test("formTouchAll / formUnTouchAll", () => {
const f = {
a: new TextField("a", validators.required(isRequiredMessage)),
b: {
c: {
d: new TextField("d", validators.required(isRequiredMessage)),
k: null,
},
},
e: [new TextField("")],
d: new ListField<number>([]),
};

expect(isFormTouched(f)).toBeFalsy();
expect(isFormTouched(f)).toBeFalsy();
expect(isFormTouched(f.b.c)).toBeFalsy();

formTouchAll(f);

expect(isFormTouched(f)).toBeTruthy();
expect(isFormTouched(f)).toBeTruthy();
expect(isFormTouched(f.b.c)).toBeTruthy();

formUnTouchAll(f);

expect(isFormTouched(f)).toBeFalsy();
expect(isFormTouched(f)).toBeFalsy();
expect(isFormTouched(f.b.c)).toBeFalsy();

f.e[0].touch();
expect(isFormTouched(f)).toBeTruthy();

formUnTouchAll(f);
expect(isFormTouched(f)).toBeFalsy();

f.d.add(1);
expect(isFormTouched(f)).toBeTruthy();

formUnTouchAll(f);
expect(isFormTouched(f)).toBeFalsy();
});
Loading

0 comments on commit c40b085

Please sign in to comment.