sendou.ink/app/features/user-page/actions/u.$identifier.builds.server.ts
2025-10-19 11:57:53 +03:00

73 lines
1.8 KiB
TypeScript

import { type ActionFunction, redirect } from "@remix-run/node";
import { z } from "zod/v4";
import { BUILD_SORT_IDENTIFIERS } from "~/db/tables";
import { requireUser } from "~/features/auth/core/user.server";
import * as BuildRepository from "~/features/builds/BuildRepository.server";
import * as UserRepository from "~/features/user-page/UserRepository.server";
import { errorToastIfFalsy, parseRequestPayload } from "~/utils/remix.server";
import { assertUnreachable } from "~/utils/types";
import { userBuildsPage } from "~/utils/urls";
import {
_action,
actualNumber,
emptyArrayToNull,
id,
processMany,
removeDuplicates,
safeJSONParse,
} from "~/utils/zod";
export const action: ActionFunction = async ({ request }) => {
const user = await requireUser(request);
const data = await parseRequestPayload({
request,
schema: buildsActionSchema,
});
switch (data._action) {
case "DELETE_BUILD": {
const usersBuilds = await BuildRepository.allByUserId(user.id, {
showPrivate: true,
});
const buildToDelete = usersBuilds.find(
(build) => build.id === data.buildToDeleteId,
);
errorToastIfFalsy(buildToDelete, "Build to delete not found");
await BuildRepository.deleteById(data.buildToDeleteId);
break;
}
case "UPDATE_SORTING": {
await UserRepository.updateBuildSorting({
userId: user.id,
buildSorting: data.buildSorting,
});
break;
}
default: {
assertUnreachable(data);
}
}
return redirect(userBuildsPage(user));
};
const buildsActionSchema = z.union([
z.object({
_action: _action("DELETE_BUILD"),
buildToDeleteId: z.preprocess(actualNumber, id),
}),
z.object({
_action: _action("UPDATE_SORTING"),
buildSorting: z.preprocess(
processMany(safeJSONParse, removeDuplicates, emptyArrayToNull),
z.array(z.enum(BUILD_SORT_IDENTIFIERS)).nullable(),
),
}),
]);