feat(api): name imports after their person and keep locked documents' details

An imported resume is named from its content (the person's name, else the
headline) instead of a random name. Renaming, tagging or linking a locked
document is refused, as moving it to Trash already was.
This commit is contained in:
Amruth Pillai
2026-09-28 20:38:32 +02:00
parent a568f64b43
commit d4406c729b
2 changed files with 20 additions and 7 deletions
+17 -6
View File
@@ -73,6 +73,12 @@ async function readState(ref: DocumentRef) {
return row;
}
async function assertUnlocked(ref: DocumentRef) {
const state = await readState(ref);
if (state.isLocked) throw locked(ref.type);
return state;
}
/** Deletes for good: resumes through their own path (storage too), letters directly. */
async function deleteForGood(ref: DocumentRef) {
if (ref.type === "resume") return resumeService.delete({ id: ref.id, userId: ref.userId });
@@ -189,11 +195,16 @@ export const documentsService = {
};
},
/** A name typed by hand ends a blank resume's automatic naming. */
rename: (input: DocumentRef & { name: string }) =>
update(input, { name: input.name, autoName: false }, { name: input.name }),
/** A name typed by hand ends a blank resume's automatic naming. Locked documents keep their details. */
rename: async (input: DocumentRef & { name: string }) => {
await assertUnlocked(input);
await update(input, { name: input.name, autoName: false }, { name: input.name });
},
setTags: (input: DocumentRef & { tags: string[] }) => update(input, { tags: [...new Set(input.tags)] }),
setTags: async (input: DocumentRef & { tags: string[] }) => {
await assertUnlocked(input);
await update(input, { tags: [...new Set(input.tags)] });
},
setLocked: (input: DocumentRef & { isLocked: boolean }) =>
input.type === "resume"
@@ -201,14 +212,14 @@ export const documentsService = {
: update(input, { isLocked: input.isLocked }),
linkApplication: async (input: DocumentRef & { applicationId: string | null }) => {
await assertUnlocked(input);
if (input.applicationId) await assertOwnedApplication(input.userId, input.applicationId);
await update(input, { applicationId: input.applicationId }, { sourceApplicationId: input.applicationId });
},
/** Undoable: Trash hides the document and stops its public link; Restore brings it back intact. */
trash: async (input: DocumentRef) => {
const state = await readState(input);
if (state.isLocked) throw locked(input.type);
await assertUnlocked(input);
await update(input, { trashedAt: new Date() });
},
+3 -1
View File
@@ -103,9 +103,11 @@ export const crudRouter = {
.handler(async ({ context, input }) => {
const id = generateId();
// Named from the file's content (the person, else the headline) so nothing needs filling in first.
const name = input.data.basics.name.trim() || input.data.basics.headline.trim() || generateRandomName();
await resumeService.create({
id,
name: generateRandomName(),
name: name.slice(0, 100),
tags: [],
data: input.data,
locale: context.locale,