Adding specializations to the public facing database page

This commit is contained in:
jb
2026-07-30 02:42:23 -04:00
parent 65a4987cad
commit 3f440dd3c9
5 changed files with 158 additions and 214 deletions
+82 -197
View File
@@ -2,248 +2,133 @@
## Parallel task execution ## Parallel task execution
For substantial changes, use subagents as a standard practice when the work can For substantial changes, use subagents as a standard practice when the work can be divided into concrete, independent tasks that provide real parallel progress.
be divided into concrete, independent tasks that provide real parallel progress.
1. Delegate bounded mechanical work to faster, lower-reasoning agents. Suitable 1. Delegate bounded mechanical work to faster, lower-reasoning agents (focused code discovery, repetitive call-site updates, documentation, isolated test coverage).
tasks include focused code discovery, repetitive call-site updates, 2. Keep architecture, security-sensitive behavior, database migrations, shared data-model decisions, and complex debugging with a suitably capable agent.
documentation, and isolated test coverage. 3. Assign explicit file or subsystem ownership before parallel edits. Avoid having multiple agents edit the same files unless their work is deliberately sequenced.
2. Keep architecture, security-sensitive behavior, database migrations, shared 4. Do not delegate tightly coupled work merely to increase agent count. Keep it with the primary agent when coordination would cost more than it saves.
data-model decisions, and complex debugging with a suitably capable agent. 5. The primary agent remains responsible for integrating results, reviewing diffs, resolving cross-cutting concerns, and running the final required validation.
3. Assign explicit file or subsystem ownership before parallel edits. Avoid 6. Subagents must follow every repository rule that applies to their assigned work, including design-system maintenance, audit logging, and database preservation.
having multiple agents edit the same files unless their work is deliberately
sequenced.
4. Do not delegate tightly coupled work merely to increase agent count. Keep it
with the primary agent when coordination would cost more than it saves.
5. The primary agent remains responsible for integrating results, reviewing
diffs, resolving cross-cutting concerns, and running the final required
validation.
6. Subagents must follow every repository rule that applies to their assigned
work, including design-system maintenance, audit logging, and database
preservation.
## Documentation prose ## Documentation prose
Use the `stop-slop` skill whenever an agent creates or changes project Use the `stop-slop` skill whenever an agent creates or changes project documentation. Apply its review to the complete affected document, including sections that the change did not introduce. Preserve technical meaning, commands, paths, formulas, links, policy strength, and product terminology. Remove filler, vague claims, formulaic contrasts, manufactured emphasis, false agency, and repetition. Prefer active voice and specific subjects. Do not weaken terms such as `must`, `never`, or `always` when they express a real requirement.
documentation. Apply its review to the complete affected document, including
sections that the change did not introduce.
Preserve technical meaning, commands, paths, formulas, links, policy strength, ## Available skills
and product terminology. Remove filler, vague claims, formulaic contrasts,
manufactured emphasis, false agency, and repetition. Prefer active voice and
specific subjects. Do not weaken terms such as `must`, `never`, or `always`
when they express a real requirement.
## SHD design-system maintenance The `.agents/skills/` directory contains repo-specific skill files. Key skills:
Treat `docs/DESIGN_SYSTEM.md` and the shared tokens and patterns in `src/app.css` - `stop-slop` — apply to documentation prose changes
as maintained parts of the product. - `frontend-design` / `impeccable` — UI design, refinement, and polish
- `import-talent-screenshot` / `import-weapon-screenshot` — extract Division Resurgence game data from screenshots and enter through ISAC
- `webapp-testing` — Playwright-based local web app testing
Whenever a change makes a significant visual or interaction-design decision, Use `skill` tool to load one when the task matches.
update the design system in the same change. This includes:
- introducing or changing colors, typography, spacing, borders, elevation, or motion; ## Verify commands
- creating a new reusable layout, navigation, table, editor, form, card, status, or feedback pattern;
- materially changing dashboard density, hierarchy, responsive behavior, or accessibility behavior;
- changing the semantic meaning of an existing visual state;
- establishing a pattern that future pages or components should follow.
For significant design changes: Run in order after source changes:
1. Reuse or extend the CSS custom properties in `src/app.css`; do not scatter ```
near-duplicate hard-coded values throughout new components. npm run check # svelte-kit sync + svelte-check (typecheck)
2. Update `docs/DESIGN_SYSTEM.md` with the new or revised rule, token, pattern, npm run build # dotenvx production env + vite build
usage guidance, and responsive or accessibility considerations. ```
3. Refactor repeated UI into a reusable component when that makes the pattern
clearer and reduces divergence.
4. Preserve the SHD design principles: tactical but restrained, data-first,
square and engineered, orange reserved for action and emphasis, and explicit
textual state indicators.
5. Verify hover, pressed, focus-visible, pending, disabled, error, success, and
reduced-motion behavior where applicable.
6. Run `npm run check` and `npm run build` before completing the change.
Minor alignment fixes, isolated bug fixes, and corrections that do not introduce `npm run check` runs `svelte-kit sync` (generates `.svelte-kit/tsconfig.json`) then `svelte-check --tsconfig ./tsconfig.json`. `npm test` runs all vitest tests; there is no separate vitest config (vitest inherits from `vite.config.ts`).
or revise a reusable design decision do not require a documentation update.
If uncertain whether a visual change is significant, update the design system. Test files (`*.test.ts`) live adjacent to the code they test, mostly in `src/lib/`. Most server-unit tests mock `$lib/server/database` via `vi.mock(...)` — unnecessary for unit tests against pure logic. Integration tests that need database state use the embedded Turso engine.
## Reusable interface patterns ## Framework and toolchain quirks
Favor shared, generic components whenever a visual or interaction pattern is - **SvelteKit 2 / Svelte 5** with forced runes mode (configured in `vite.config.ts` via plugin `compilerOptions.runes`). There is no `svelte.config.js` — SvelteKit config lives in `vite.config.ts`.
used in more than one place or is likely to recur. Before adding page-local - **Node adapter** (`@sveltejs/adapter-node`). Production entry point is `node build`. The container runs migrations on startup, then serves.
markup and styles, check `src/lib/components` and extend an appropriate shared - **Embedded Turso** (`@tursodatabase/database`) — local SQLite, no Turso Cloud, no libSQL HTTP, no replicas. The dev database path is `./data/shd-build-development.db` (set in `.env.development`).
component when practical. When introducing a reusable pattern, migrate the - **Passkey auth** (`@simplewebauthn`) is preferred. `APP_ORIGIN` and `PASSKEY_RP_ID` must match the public origin.
clearest existing duplicate in the same change so the component has more than - **Environment files** are encrypted with `dotenvx`. Committed `.env.development`, `.env.staging`, `.env.production` are encrypted; `.env.keys` is git-ignored. The pre-commit hook runs `npm run env:stage` (encrypts and git-adds). To edit: `npm run env:decrypt`, make changes, `npm run env:stage`.
one real consumer. - **Argon2id** password hashing with server-side sessions and CSRF protection.
- **ISAC namespace**: `/isac` is the admin console; `/admin` redirects there via 308.
For small, finite option sets, prefer the shared segmented-choice pattern over
a dropdown. Use radio semantics for exactly one choice and checkbox semantics
for additive independent choices. Reserve dropdowns for long, dynamic, or
search-oriented option sets. Preserve native form semantics, keyboard access,
focus-visible, selected, disabled, pressed, responsive, and reduced-motion
behavior in shared controls.
## Development server lifecycle ## Development server lifecycle
After changing source code, styles, configuration, dependencies, or routes, After changing source code, styles, configuration, dependencies, or routes, restart the development server before handing work back.
restart the development server before handing work back to the project owner.
1. Inspect running processes and stop only this project's development-server 1. Inspect running processes and stop only this project's development-server process tree.
process tree. 2. Start one hidden `npm run dev` process from the repository root. Do not leave duplicate project development servers running.
2. Start one hidden `npm run dev` process from the repository root. Do not leave 3. Confirm the server accepts an HTTP request before completing the task.
duplicate project development servers running. 4. Leave the verified development server running so the project owner can review the changes.
3. Confirm that the server accepts an HTTP request before completing the task.
4. Leave the verified development server running so the project owner can
review the changes.
Database maintenance may require the server to remain stopped while the Database maintenance may require the server to remain stopped while the embedded database is locked. Restart and verify the server after maintenance completes.
embedded database is locked or changing. Restart and verify the server after
the maintenance and validation steps finish.
## Prototype compatibility ## SHD design-system maintenance
Treat application data and client payloads as prototype formats until the Treat `docs/DESIGN_SYSTEM.md` and the shared tokens in `src/app.css` as maintained parts of the product. When a change makes a significant visual or interaction-design decision, update the design system in the same change.
project owner declares a production-stable format. During the prototype phase:
1. Prefer one current validated schema over compatibility readers, repair For significant design changes: reuse or extend CSS custom properties from `src/app.css`; update `docs/DESIGN_SYSTEM.md`; refactor repeated UI into a reusable component; verify hover, pressed, focus-visible, pending, disabled, error, success, and reduced-motion behavior. Run `npm run check && npm run build` before completing.
branches, dual writes, or fallback parsing for obsolete formats.
2. Remove incompatible local drafts, saved builds, calibration snapshots, and
other derived records through a backed-up migration when the current model
cannot represent them safely.
3. Preserve authored game data whenever the new model can retain it without
guessing. Mark incomplete mechanics for configuration instead of inventing
trigger parameters or effect values.
4. Follow the database-preservation rules before removing or reshaping
persistent data.
5. Add compatibility support only when the project owner requires it for a
production-stable format.
## Talent mechanic conventions Design principles: tactical but restrained, data-first, square and engineered, orange reserved for action/emphasis, explicit textual state indicators.
Before importing a Talent or changing its mechanic program, read Minor alignment fixes, isolated bug fixes, and corrections that do not introduce a reusable design decision do not require a documentation update.
`docs/TALENT_MECHANIC_RULES.md`. Treat the confirmed lifecycle, trigger,
cooldown, and targeting rules in that document as authoritative game behavior.
Do not mark a mechanic `requires_assumptions` for behavior that the document
resolves. Do not extend a rule to Talents outside its stated scope.
When the project owner clarifies another Talent mechanic, record the answer in ## Reusable interface patterns
`docs/TALENT_MECHANIC_RULES.md`. Keep unresolved questions separate from
confirmed behavior. Favor shared components from `src/lib/components`. Before adding page-local markup, check if a shared component covers the need. When introducing a reusable pattern, migrate the clearest existing duplicate in the same change.
For small, finite option sets, prefer the shared segmented-choice pattern over a dropdown. Use radio semantics for exactly one choice and checkbox semantics for additive independent choices. Reserve dropdowns for long, dynamic, or search-oriented option sets.
## ISAC editor lifecycle ## ISAC editor lifecycle
All ISAC data-management editors must follow the same successful-save behavior: All ISAC data-management editors must follow the same successful-save behavior:
1. Saving an edit to an existing record closes the editor and returns the user 1. Saving an edit to an existing record closes the editor and returns the user to the registry.
to the registry. 2. Saving a newly added record keeps the editor open with a fresh blank record.
2. Saving a newly added record keeps the editor open with a fresh blank record
so consecutive data entry remains fast.
3. Validation errors keep the editor open and preserve the entered values. 3. Validation errors keep the editor open and preserve the entered values.
4. Server actions must return the add/edit mode and a unique result token; 4. Server actions must return the add/edit mode and a unique result token; client-side reactive handling must consume each token only once to avoid repeated state updates.
client-side reactive handling must consume each token only once to avoid 5. When a successful add keeps an icon-enabled editor open, increment the shared upload reset token so the file input, preview, filename, drag/paste state, validation message, and removal state are fresh for the next record.
repeated state updates.
5. When a successful add keeps an icon-enabled editor open, increment the
shared upload reset token so the native file input, generated preview,
filename, drag/paste state, validation message, and removal state are fresh
for the next record.
Apply this convention whenever an ISAC editor is added or materially changed. Apply this convention whenever an ISAC editor is added or materially changed.
## ISAC audit logging ## ISAC audit logging
Every successful server-side mutation performed through ISAC must create an Every successful server-side ISAC mutation must create an append-only audit event. Follow these rules:
append-only audit event. Audit coverage is a required part of implementing or
changing any ISAC module, action, endpoint, bulk operation, moderation control,
identity operation, access-policy change, archive action, or destructive action.
Follow these rules: 1. Use the shared audit utilities in `src/lib/server/audit.ts`; do not write directly to `audit_log`.
2. Record the authenticated operator ID, stable category, specific action (namespaced past tense, e.g. `weapon.updated`), target entity ID, timestamp, and useful before/after snapshots. Categories include `game_data`, `user_access`, `user_moderation`, `identity`, `role_management`.
3. Capture changes to the primary row and its relationships. Include meaningful previous and resulting sets for linked records.
4. Create audit records only after validation succeeds. Rejected or failed requests must not appear as successful mutation events.
5. Never include password hashes, session tokens, magic-link tokens, OAuth state, passkey public-key bytes, SMTP credentials, or environment secrets in audit snapshots.
6. Audit visibility is controlled by `audit.view`. The read-only `Auditor` role grants `isac.access` and `audit.view` without granting mutation permissions.
7. Audit reversion is a compensating action, never a history rewrite. Require `audit.revert`, confirmation, current-state comparison, relationship validation, and a new `audit.reverted` event.
8. Tests for an ISAC mutation must confirm its audit event contains the correct actor, category, action, target, and change evidence.
1. Use the shared audit utilities in `src/lib/server/audit.ts`; do not invent ## Prototype compatibility
module-specific audit formats or write directly to `audit_log` in new code.
2. Record the authenticated operator ID, stable category, specific action, Treat application data and client payloads as prototype formats until the project owner declares a production-stable format.
target entity ID, timestamp, and useful before/after snapshots.
3. Categories describe durable operational domains such as `game_data`, 1. Prefer one current validated schema over compatibility readers, repair branches, dual writes, or fallback parsing for obsolete formats.
`user_access`, `user_moderation`, `identity`, and `role_management`. 2. Remove incompatible local drafts, saved builds, calibration snapshots, and other derived records through a backed-up migration when the current model cannot represent them safely.
Actions describe the exact event in namespaced past tense, such as 3. Preserve authored game data whenever the new model can retain it without guessing.
`weapon.updated`, `attribute.archived`, or `moderation.blocked`. 4. Follow the database-preservation rules before removing or reshaping persistent data.
4. Capture changes to the primary row and its relationships. When an action 5. Add compatibility support only when the project owner requires it for a production-stable format.
replaces permission sets, compatible brands, talents, effects, tiers, or
other linked records, include the meaningful previous and resulting sets. ## Talent mechanic conventions
5. Create audit records only after validation succeeds and the requested
mutation has completed. Rejected, unauthorized, or validation-failed Before importing a Talent or changing its mechanic program, read `docs/TALENT_MECHANIC_RULES.md`. Treat the confirmed lifecycle, trigger, cooldown, and targeting rules in that document as authoritative game behavior. Do not mark a mechanic `requires_assumptions` for behavior that the document resolves. When the project owner clarifies another Talent mechanic, record the answer in `docs/TALENT_MECHANIC_RULES.md`.
requests must not appear as successful mutation events.
6. Never include password hashes, session tokens, magic-link tokens, OAuth
state, passkey public-key bytes, SMTP credentials, environment secrets, or
other authentication secrets in audit snapshots. Minimize personal data to
what is necessary for security, support, and accountability.
7. Audit visibility is controlled by `audit.view`. The read-only `Auditor` role
grants `isac.access` and `audit.view`; it must not implicitly grant mutation,
moderation, identity-management, or user-access permissions.
8. New audit categories and actions must remain searchable in the Audit History
registry. Update its grouped filters when a new durable operational domain is
introduced.
9. Preserve audit history during normal record archival and soft deletion.
Never add an ISAC action that edits or deletes audit records without an
explicit retention policy, legal review, and separately audited authority.
10. Tests and handoff verification for an ISAC mutation must include confirming
that its audit event contains the correct actor, category, action, target,
and change evidence.
11. Audit reversion is a separately authorized compensating action, never a
history rewrite. Require `audit.revert`, an explicit confirmation
interaction, current-state comparison against the original after-snapshot,
relationship validation, and a new `audit.reverted` event referencing the
original event. Block stale, already-reverted, unsupported, destructive
identity, and incomplete-snapshot events rather than guessing.
## Database preservation ## Database preservation
Development data is valuable and may be entered by non-technical contributors. Development data is valuable. Treat embedded database files as persistent user data, not disposable build artifacts.
Treat embedded database files as persistent user data, not disposable build
artifacts.
Before deleting, replacing, recreating, renaming, or destructively migrating a Before destructive work on the database, stop the dev server, then create a verified timestamped JSON backup with `npm run db:backup`. Keep backups out of Git (they can contain user records). Prefer additive, repeatable schema migrations over database recreation. Report the backup path when completing database-destructive work.
database, create a timestamped JSON backup with `npm run db:backup`. Create the
same backup before any major schema change.
Database work must follow these rules: Use `npm run db:restore -- <backup-file>` only with an empty, migrated target database — it refuses to merge into a populated database.
1. Confirm which environment and exact database path are in scope.
2. Before every database backup or restore, inspect running processes and stop
only this project's development-server process tree. The embedded Turso
database holds an exclusive file lock while the dev server is running, so do
not attempt the backup first and wait for a locking failure.
3. Create and verify a JSON backup before destructive work.
4. Never overwrite or delete the only available backup.
5. Keep backups out of Git; they can contain user records and password hashes.
6. Prefer additive, repeatable schema migrations over database recreation.
7. After a restore, run the schema migration and verify record counts and
critical entities before resuming data entry.
8. Report the backup path when completing database-destructive work.
Use `npm run db:restore -- <backup-file>` only with an empty, migrated target
database. The restore script intentionally refuses to merge into a populated
database.
## Development database maintenance ## Development database maintenance
The development server is no longer a shared contributor-facing service, so 1. Stop only this project's development-server process tree.
development database work does not need to optimize for minimum downtime. 2. Run and verify `npm run db:backup`. Retain and report the backup path.
Database safety remains mandatory: 3. Apply the migration or maintenance operation. Verify schema state, record counts, and critical entities.
4. If an operation fails, preserve logs and the verified backup rather than improvising against the database.
5. Restart the dev server, then confirm a basic health or page check.
1. Prefer additive, repeatable migrations and finish the migration logic before Production and staging backups and deployment are defined in `docs/DEPLOYMENT.md`.
operating on persistent data.
2. Identify the exact environment and database path, then inspect running
processes and stop only this project's development-server process tree.
3. Run and verify `npm run db:backup` before migration, export, restore, or
destructive schema work. Retain and report the backup path.
4. Apply the migration or maintenance operation and verify schema state, record
counts, and critical entities before restarting the server.
5. If an operation fails, preserve logs and the verified backup rather than
repeatedly improvising against the database.
6. Restart the development server when maintenance and useful verification are
complete, then confirm a basic health or page check.
Production and staging remain persistent shared environments. Their deployment,
backup, synchronization, and maintenance safeguards are defined separately in
`docs/DEPLOYMENT.md`.
+2 -1
View File
@@ -6,7 +6,8 @@ export const databaseCategories = [
{ slug: 'os-protocols', label: 'OS Protocols', description: 'Quality, core profile, primary effect, and unique upgradeable talent.' }, { slug: 'os-protocols', label: 'OS Protocols', description: 'Quality, core profile, primary effect, and unique upgradeable talent.' },
{ slug: 'talents', label: 'Talents', description: 'Equipment and weapon talents with tiered descriptions and combat mechanics.' }, { slug: 'talents', label: 'Talents', description: 'Equipment and weapon talents with tiered descriptions and combat mechanics.' },
{ slug: 'brands', label: 'Brands', description: 'Brand Set identities and their two-, three-, and four-piece bonuses.' }, { slug: 'brands', label: 'Brands', description: 'Brand Set identities and their two-, three-, and four-piece bonuses.' },
{ slug: 'skill-chips', label: 'Skill Chips', description: 'Set bonuses, primary and secondary pools, and quality-scaled unique talents.' } { slug: 'skill-chips', label: 'Skill Chips', description: 'Set bonuses, primary and secondary pools, and quality-scaled unique talents.' },
{ slug: 'specializations', label: 'Specializations', description: 'Specialization identities, focus paths, signature abilities, and tree node progression.' }
] as const; ] as const;
export type DatabaseCategorySlug = typeof databaseCategories[number]['slug']; export type DatabaseCategorySlug = typeof databaseCategories[number]['slug'];
+25 -8
View File
@@ -11,7 +11,8 @@ const tables = {
'os-protocols': 'os_protocols', 'os-protocols': 'os_protocols',
talents: 'talents', talents: 'talents',
brands: 'brand_sets', brands: 'brand_sets',
'skill-chips': 'skill_chip_sets' 'skill-chips': 'skill_chip_sets',
specializations: 'specializations'
} as const; } as const;
export const load: PageServerLoad = async ({ url }) => { export const load: PageServerLoad = async ({ url }) => {
@@ -89,14 +90,30 @@ export const load: PageServerLoad = async ({ url }) => {
SELECT 'skill-chips','Skill Chips',item.name,COALESCE(talent.name,'Skill Chip set') SELECT 'skill-chips','Skill Chips',item.name,COALESCE(talent.name,'Skill Chip set')
FROM skill_chip_sets item FROM skill_chip_sets item
LEFT JOIN skill_chip_talents talent ON talent.skill_chip_set_id=item.id LEFT JOIN skill_chip_talents talent ON talent.skill_chip_set_id=item.id
WHERE item.active=1 AND ( WHERE item.active=1 AND (
item.name LIKE ? ESCAPE '\\' COLLATE NOCASE OR talent.name LIKE ? ESCAPE '\\' COLLATE NOCASE item.name LIKE ? ESCAPE '\\' COLLATE NOCASE OR talent.name LIKE ? ESCAPE '\\' COLLATE NOCASE
OR talent.description_template LIKE ? ESCAPE '\\' COLLATE NOCASE OR talent.description_template LIKE ? ESCAPE '\\' COLLATE NOCASE
)
UNION ALL
SELECT 'specializations','Specializations',item.name,'Specialization'
FROM specializations item
WHERE item.active=1 AND (
item.name LIKE ? ESCAPE '\\' COLLATE NOCASE OR item.description LIKE ? ESCAPE '\\' COLLATE NOCASE
OR EXISTS (
SELECT 1 FROM specialization_focuses focus
WHERE focus.specialization_id=item.id AND focus.active=1
AND focus.name LIKE ? ESCAPE '\\' COLLATE NOCASE
) )
) matches OR EXISTS (
ORDER BY name COLLATE NOCASE SELECT 1 FROM specialization_abilities ability
LIMIT 40 WHERE ability.specialization_id=item.id AND ability.active=1
`)).all(...Array(23).fill(pattern)) AND ability.name LIKE ? ESCAPE '\\' COLLATE NOCASE
)
)
) matches
ORDER BY name COLLATE NOCASE
LIMIT 40
`)).all(...Array(27).fill(pattern))
: []; : [];
return { return {
registries: counts, registries: counts,
+1 -1
View File
@@ -5,7 +5,7 @@
<svelte:head> <svelte:head>
<title>The Division Resurgence database — SHD.build</title> <title>The Division Resurgence database — SHD.build</title>
<meta name="description" content="Community-maintained The Division Resurgence attributes, talents, brands, gear, weapons, mods, protocols, and Skill Chip data."/> <meta name="description" content="Community-maintained The Division Resurgence attributes, talents, brands, gear, weapons, mods, protocols, skill chips, and specializations data."/>
</svelte:head> </svelte:head>
<main class="page community-index-page"> <main class="page community-index-page">
+48 -7
View File
@@ -340,10 +340,35 @@ export const load: PageServerLoad = async ({ params, url }) => {
LEFT JOIN specialization_abilities abilities ON abilities.id=item.specialization_ability_id LEFT JOIN specialization_abilities abilities ON abilities.id=item.specialization_ability_id
LEFT JOIN specializations ON specializations.id=abilities.specialization_id LEFT JOIN specializations ON specializations.id=abilities.specialization_id
LEFT JOIN game_data_icons icons ON icons.entity_type='skill_chip' AND icons.entity_id=item.id LEFT JOIN game_data_icons icons ON icons.entity_type='skill_chip' AND icons.entity_id=item.id
WHERE item.active=1${searchClause} WHERE item.active=1${searchClause}
ORDER BY item.name COLLATE NOCASE LIMIT ? OFFSET ?`; ORDER BY item.name COLLATE NOCASE LIMIT ? OFFSET ?`;
break; break;
} case 'specializations':
countSql = `SELECT COUNT(*) AS total FROM specializations item WHERE item.active=1${searchClause}`;
rowsSql = `SELECT item.*,
(SELECT COUNT(*) FROM specialization_focuses focus WHERE focus.specialization_id=item.id AND focus.active=1) AS focus_count,
(SELECT COUNT(*) FROM specialization_focuses focus WHERE focus.specialization_id=item.id AND focus.builder_ready=1 AND focus.active=1) AS ready_focus_count,
(SELECT COUNT(*) FROM specialization_abilities ability WHERE ability.specialization_id=item.id AND ability.active=1) AS ability_count,
(SELECT COUNT(*) FROM specialization_tree_nodes node
JOIN specialization_focuses node_focus ON node_focus.id=node.focus_id
WHERE node_focus.specialization_id=item.id AND node_focus.active=1 AND node.active=1) AS node_count,
(SELECT GROUP_CONCAT(name, ', ') FROM (
SELECT focus.name FROM specialization_focuses focus
WHERE focus.specialization_id=item.id AND focus.active=1
ORDER BY focus.display_order,focus.name COLLATE NOCASE
)) AS focus_names,
(SELECT GROUP_CONCAT(name, ', ') FROM (
SELECT ability.name FROM specialization_abilities ability
WHERE ability.specialization_id=item.id AND ability.active=1
ORDER BY ability.name COLLATE NOCASE
)) AS ability_names,
icons.updated_at AS icon_updated_at
FROM specializations item
LEFT JOIN game_data_icons icons ON icons.entity_type='specialization' AND icons.entity_id=item.id
WHERE item.active=1${searchClause}
ORDER BY item.name COLLATE NOCASE LIMIT ? OFFSET ?`;
break;
}
const total = Number((await (await database.prepare(countSql)).get(...queryArgs))?.total ?? 0); const total = Number((await (await database.prepare(countSql)).get(...queryArgs))?.total ?? 0);
const pagination = getPagination(url, total); const pagination = getPagination(url, total);
@@ -575,9 +600,25 @@ export const load: PageServerLoad = async ({ params, url }) => {
{ label: 'PRIMARY POOL', value: `${row.primary_count} OPTIONS` }, { label: 'PRIMARY POOL', value: `${row.primary_count} OPTIONS` },
{ label: 'SECONDARY POOL', value: `${row.secondary_count} OPTIONS` } { label: 'SECONDARY POOL', value: `${row.secondary_count} OPTIONS` }
], ],
lines: [{ label: '3-PIECE TALENT', value: list(row.talent_name,'NOT CONFIGURED') }] lines: [{ label: '3-PIECE TALENT', value: list(row.talent_name,'NOT CONFIGURED') }]
}; };
} case 'specializations':
return {
id: String(row.id), name: String(row.name), kicker: 'SPECIALIZATION // FOCUS PATHS',
iconUrl: iconUrl('specialization',row.id,row.icon_updated_at),
badges: Number(row.ready_focus_count) ? [{ label: 'BUILDER READY' }] : [],
facts: [
{ label: 'FOCUS PATHS', value: String(row.focus_count) },
{ label: 'ABILITIES', value: String(row.ability_count) },
{ label: 'TREE NODES', value: String(row.node_count) }
],
lines: [
{ label: 'FOCUS PATHS', value: list(row.focus_names) },
{ label: 'ABILITIES', value: list(row.ability_names) }
],
description: row.description ? String(row.description) : undefined
};
}
}); });
return { category, items, pagination, search, filters }; return { category, items, pagination, search, filters };