This commit is contained in:
seb
2026-07-31 09:09:34 +02:00
parent 84a7c6f7ef
commit 818d799f63
6 changed files with 21 additions and 38 deletions

View File

@@ -120,8 +120,7 @@ Backend mapping:
| Method | Signature | Returns | | Method | Signature | Returns |
| --- | --- | --- | | --- | --- | --- |
| `createUnderParent` | `({ googleAuthId, parentId, name })` | `{ id, parentId, sort, name }` | | `createUnderParent` | `({ googleAuthId, parentId, name })` | `{ id, parentId, sort, name }` |
| `updateParentId` | `(id, parentId)` | `void` | | `delete` | `({ googleAuthId, id })` | `void` — blocked by FK if `categoryarticle` or child categories remain |
| `delete` | `(id)` | `void` — blocked by FK if `categoryarticle` (or child categories) still reference it |
| `getTreeForCompany` | `(googleAuthId, companyId)` | `{ languageId, tree, articles }` | | `getTreeForCompany` | `(googleAuthId, companyId)` | `{ languageId, tree, articles }` |
### `createUnderParent` (transaction) ### `createUnderParent` (transaction)
@@ -131,6 +130,8 @@ Backend mapping:
3. `companyuser` for `(googleAuthId, companyId)`, else `Not authorized` 3. `companyuser` for `(googleAuthId, companyId)`, else `Not authorized`
4. Insert `category` (next sibling `sort`) + `categorytext` (`name`, slugified `slug`, company default language) 4. Insert `category` (next sibling `sort`) + `categorytext` (`name`, slugified `slug`, company default language)
All category mutations use `resolveCategoryAccess(googleAuthId, categoryId)` internally. Root category cannot be deleted.
`getTreeForCompany` throws `Not authorized` if `googleAuthId` is not a `companyuser` of `companyId`, and `Company not found` if the company is missing, has no `root_category_id`, or no `default_language_id`. `getTreeForCompany` throws `Not authorized` if `googleAuthId` is not a `companyuser` of `companyId`, and `Company not found` if the company is missing, has no `root_category_id`, or no `default_language_id`.
--- ---
@@ -146,6 +147,7 @@ Backend mapping:
| `PUT /api/auth/selected-company` | `google_auth.setSelectedCompany(googleAuthId, companyId)` | | `PUT /api/auth/selected-company` | `google_auth.setSelectedCompany(googleAuthId, companyId)` |
| `GET /api/category/tree?companyId=` | `category.getTreeForCompany(googleAuthId, companyId)` | | `GET /api/category/tree?companyId=` | `category.getTreeForCompany(googleAuthId, companyId)` |
| `POST /api/category` | `category.createUnderParent({ googleAuthId, parentId, name })` | | `POST /api/category` | `category.createUnderParent({ googleAuthId, parentId, name })` |
| `DELETE /api/category/:id` | `category.delete({ googleAuthId, id })` |
--- ---

View File

@@ -96,8 +96,7 @@ const MENU = {
label: 'category', label: 'category',
items: [ items: [
{ value: 'category.createUnderParent', label: 'createUnderParent', hint: 'googleAuthId, parentId, name → row' }, { value: 'category.createUnderParent', label: 'createUnderParent', hint: 'googleAuthId, parentId, name → row' },
{ value: 'category.updateParentId', label: 'updateParentId', hint: 'id, parentId' }, { value: 'category.delete', label: 'delete', hint: 'googleAuthId, id' },
{ value: 'category.delete', label: 'delete', hint: 'id' },
{ value: 'category.getTreeForCompany', label: 'getTreeForCompany', hint: 'googleAuthId, companyId → rows' }, { value: 'category.getTreeForCompany', label: 'getTreeForCompany', hint: 'googleAuthId, companyId → rows' },
], ],
}, },
@@ -320,23 +319,15 @@ async function runMethod(client, action) {
}), }),
]; ];
} }
case 'category.updateParentId': {
const id = await askInt('Category id');
if (id == null) return;
const parentRaw = await askText('parentId (null as empty)', {
placeholder: '',
validate: (v) =>
v?.trim() && !/^\d+$/.test(v.trim()) ? 'Integer or empty' : undefined,
});
if (parentRaw == null) return;
const parentId = parentRaw === '' ? null : Number(parentRaw);
await client.category.updateParentId(Number(id), parentId);
return ['ok', true];
}
case 'category.delete': { case 'category.delete': {
const googleAuthId = await askInt('google_auth id');
if (googleAuthId == null) return;
const id = await askInt('Category id'); const id = await askInt('Category id');
if (id == null) return; if (id == null) return;
await client.category.delete(Number(id)); await client.category.delete({
googleAuthId: Number(googleAuthId),
id: Number(id),
});
return ['ok', true]; return ['ok', true];
} }
case 'category.getTreeForCompany': { case 'category.getTreeForCompany': {

View File

@@ -7,7 +7,6 @@ import companyDelete from './store/company/delete.js';
import companyList from './store/company/list.js'; import companyList from './store/company/list.js';
import categoryGetTreeForCompany from './store/category/getTreeForCompany.js'; import categoryGetTreeForCompany from './store/category/getTreeForCompany.js';
import categoryCreateUnderParent from './store/category/createUnderParent.js'; import categoryCreateUnderParent from './store/category/createUnderParent.js';
import categoryUpdateParentId from './store/category/updateParentId.js';
import categoryDelete from './store/category/delete.js'; import categoryDelete from './store/category/delete.js';
import langCreate from './store/lang/create.js'; import langCreate from './store/lang/create.js';
import langDelete from './store/lang/delete.js'; import langDelete from './store/lang/delete.js';
@@ -46,7 +45,6 @@ export default class LibDashStore {
this.category = { this.category = {
getTreeForCompany: categoryGetTreeForCompany(pool), getTreeForCompany: categoryGetTreeForCompany(pool),
createUnderParent: categoryCreateUnderParent(pool), createUnderParent: categoryCreateUnderParent(pool),
updateParentId: categoryUpdateParentId(pool),
delete: categoryDelete(pool), delete: categoryDelete(pool),
}; };

View File

@@ -126,12 +126,6 @@ FROM category
WHERE parent_id = @parentId WHERE parent_id = @parentId
`; `;
export const UPDATE_PARENT_ID = `
UPDATE category
SET parent_id = @parentId
WHERE id = @id
`;
export const DELETE = ` export const DELETE = `
DELETE FROM category DELETE FROM category
WHERE id = @id WHERE id = @id

View File

@@ -1,7 +1,16 @@
import { DELETE } from '../../sql/category.js'; import { DELETE } from '../../sql/category.js';
import resolveCategoryAccess from './resolveCategoryAccess.js';
export default function del(pool) { export default function del(pool) {
return async (id) => { const access = resolveCategoryAccess(pool);
return async ({ googleAuthId, id }) => {
const { rootCategoryId } = await access(googleAuthId, id);
if (id === rootCategoryId) {
throw new Error('Cannot delete root category');
}
await pool.request().input('id', id).query(DELETE); await pool.request().input('id', id).query(DELETE);
}; };
} }

View File

@@ -1,11 +0,0 @@
import { UPDATE_PARENT_ID } from '../../sql/category.js';
export default function updateParentId(pool) {
return async (id, parentId) => {
await pool
.request()
.input('id', id)
.input('parentId', parentId)
.query(UPDATE_PARENT_ID);
};
}