feat: refine database discovery and ISAC interactions
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { getDatabase } from '$lib/server/database';
|
||||
import { getRegistrySearch } from '$lib/server/pagination';
|
||||
import { databaseCategories } from '$lib/database/categories';
|
||||
import type { PageServerLoad } from './$types';
|
||||
|
||||
@@ -13,14 +14,99 @@ const tables = {
|
||||
'skill-chips': 'skill_chip_sets'
|
||||
} as const;
|
||||
|
||||
export const load: PageServerLoad = async () => {
|
||||
export const load: PageServerLoad = async ({ url }) => {
|
||||
const database = await getDatabase();
|
||||
const { search, pattern } = getRegistrySearch(url);
|
||||
const counts = await Promise.all(databaseCategories.map(async (category) => {
|
||||
const row = await (await database.prepare(`SELECT COUNT(*) AS count FROM ${tables[category.slug]} WHERE active=1`)).get();
|
||||
return { ...category, count: Number(row?.count ?? 0) };
|
||||
}));
|
||||
const searchResults = search
|
||||
? await (await database.prepare(`
|
||||
SELECT * FROM (
|
||||
SELECT 'gear' AS slug,'Gear' AS category,item.name,
|
||||
item.slot || ' · T' || item.tier AS detail
|
||||
FROM gear item
|
||||
WHERE item.active=1 AND (item.name LIKE ? ESCAPE '\\' COLLATE NOCASE OR item.slot LIKE ? ESCAPE '\\' COLLATE NOCASE)
|
||||
UNION ALL
|
||||
SELECT 'weapons','Weapons',item.name,
|
||||
item.category || ' · ' || item.damage_type || ' damage'
|
||||
FROM weapons item
|
||||
WHERE item.active=1 AND (
|
||||
item.name LIKE ? ESCAPE '\\' COLLATE NOCASE OR item.category LIKE ? ESCAPE '\\' COLLATE NOCASE
|
||||
OR item.damage_type LIKE ? ESCAPE '\\' COLLATE NOCASE
|
||||
)
|
||||
UNION ALL
|
||||
SELECT 'weapon-mods','Weapon Mods',item.name,item.slot_type
|
||||
FROM weapon_mods item
|
||||
WHERE item.active=1 AND (item.name LIKE ? ESCAPE '\\' COLLATE NOCASE OR item.slot_type LIKE ? ESCAPE '\\' COLLATE NOCASE)
|
||||
UNION ALL
|
||||
SELECT 'gear-mods','Gear Mods',item.name,'Gear modification'
|
||||
FROM gear_mods item
|
||||
WHERE item.active=1 AND item.name LIKE ? ESCAPE '\\' COLLATE NOCASE
|
||||
UNION ALL
|
||||
SELECT 'os-protocols','OS Protocols',item.name,
|
||||
item.quality || ' · ' || item.primary_attribute_type
|
||||
FROM os_protocols item
|
||||
LEFT JOIN os_protocol_talents talent ON talent.protocol_id=item.id
|
||||
WHERE item.active=1 AND (
|
||||
item.name LIKE ? ESCAPE '\\' COLLATE NOCASE OR item.quality LIKE ? ESCAPE '\\' COLLATE NOCASE
|
||||
OR item.primary_attribute_type LIKE ? ESCAPE '\\' COLLATE NOCASE
|
||||
OR talent.name LIKE ? ESCAPE '\\' COLLATE NOCASE OR talent.description_template LIKE ? ESCAPE '\\' COLLATE NOCASE
|
||||
)
|
||||
UNION ALL
|
||||
SELECT 'talents','Talents',item.name,item.category
|
||||
FROM talents item
|
||||
WHERE item.active=1 AND (
|
||||
item.name LIKE ? ESCAPE '\\' COLLATE NOCASE OR item.category LIKE ? ESCAPE '\\' COLLATE NOCASE
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM talent_tiers tier
|
||||
WHERE tier.talent_id=item.id AND (
|
||||
tier.description LIKE ? ESCAPE '\\' COLLATE NOCASE
|
||||
OR tier.trigger_text LIKE ? ESCAPE '\\' COLLATE NOCASE
|
||||
OR tier.effect_text LIKE ? ESCAPE '\\' COLLATE NOCASE
|
||||
)
|
||||
)
|
||||
)
|
||||
UNION ALL
|
||||
SELECT 'brands','Brands',item.name,
|
||||
COALESCE((
|
||||
SELECT GROUP_CONCAT(attribute.name, ', ')
|
||||
FROM brand_set_effects effect
|
||||
JOIN stat_attributes attribute ON attribute.id=effect.stat_attribute_id
|
||||
WHERE effect.brand_set_id=item.id
|
||||
),'Brand Set bonuses')
|
||||
FROM brand_sets item
|
||||
WHERE item.active=1 AND (
|
||||
item.name LIKE ? ESCAPE '\\' COLLATE NOCASE
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM brand_set_effects effect
|
||||
JOIN stat_attributes attribute ON attribute.id=effect.stat_attribute_id
|
||||
WHERE effect.brand_set_id=item.id AND attribute.name LIKE ? ESCAPE '\\' COLLATE NOCASE
|
||||
)
|
||||
)
|
||||
UNION ALL
|
||||
SELECT 'skill-chips','Skill Chips',item.name,COALESCE(talent.name,'Skill Chip set')
|
||||
FROM skill_chip_sets item
|
||||
LEFT JOIN skill_chip_talents talent ON talent.skill_chip_set_id=item.id
|
||||
WHERE item.active=1 AND (
|
||||
item.name LIKE ? ESCAPE '\\' COLLATE NOCASE OR talent.name LIKE ? ESCAPE '\\' COLLATE NOCASE
|
||||
OR talent.description_template LIKE ? ESCAPE '\\' COLLATE NOCASE
|
||||
)
|
||||
) matches
|
||||
ORDER BY name COLLATE NOCASE
|
||||
LIMIT 40
|
||||
`)).all(...Array(23).fill(pattern))
|
||||
: [];
|
||||
return {
|
||||
registries: counts,
|
||||
total: counts.reduce((total, registry) => total + registry.count, 0)
|
||||
total: counts.reduce((total, registry) => total + registry.count, 0),
|
||||
search,
|
||||
searchResults: searchResults.map((result) => ({
|
||||
slug: String(result.slug),
|
||||
category: String(result.category),
|
||||
name: String(result.name),
|
||||
detail: String(result.detail ?? '')
|
||||
}))
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,28 +1,55 @@
|
||||
<script lang="ts">
|
||||
let { data } = $props();
|
||||
const onlineRegistries = $derived(data.registries.filter((registry) => registry.count > 0).length);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Game 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."/>
|
||||
</svelte:head>
|
||||
|
||||
<main class="page community-index-page">
|
||||
<header class="catalogue-hero community-index-hero">
|
||||
<div><p class="eyebrow"><span></span> ISAC PUBLIC ARCHIVE</p><h1>GAME<br/><em>DATABASE.</em></h1><p>Community-maintained Resurgence data powering SHD.build tools, loadouts, and reference material.</p></div>
|
||||
<div class="catalogue-count"><strong>{data.total}</strong><span>ACTIVE RECORDS</span><a class="primary" href="/isac">CONTRIBUTE DATA →</a></div>
|
||||
<div><p class="eyebrow"><span></span> ISAC PUBLIC ARCHIVE</p><h1>RESURGENCE <em>DATABASE.</em></h1><p>Explore equipment, talents, modifications, and gameplay effects from The Division Resurgence.</p></div>
|
||||
<div class="catalogue-count"><strong>{onlineRegistries} REGISTRIES ONLINE</strong><span>{data.total} ACTIVE RECORDS</span><a class="secondary" href="/isac">CONTRIBUTE THROUGH ISAC →</a></div>
|
||||
</header>
|
||||
<section class="community-database-intro">
|
||||
<div><span>DATABASE STATUS</span><strong>PUBLIC INDEX ONLINE</strong><small>Select a registry to inspect active, approved records and their resolved gameplay attributes.</small></div>
|
||||
<div><span>DATA STEWARDSHIP</span><strong>COMMUNITY + OVERSIGHT</strong><small>Registered agents can inspect source data and propose verified corrections through ISAC.</small></div>
|
||||
<section class="database-global-search" aria-labelledby="database-search-heading">
|
||||
<div>
|
||||
<span>DATABASE INDEX</span>
|
||||
<strong id="database-search-heading">SELECT A REGISTRY OR SEARCH ALL DATA</strong>
|
||||
</div>
|
||||
<form method="GET" action="/database" role="search">
|
||||
<label class="sr-only" for="database-global-query">Search all database categories</label>
|
||||
<input id="database-global-query" name="q" type="search" value={data.search} placeholder="SEARCH ITEMS, TALENTS, ATTRIBUTES, OR EFFECTS" maxlength="80"/>
|
||||
<button type="submit">SEARCH ALL DATA</button>
|
||||
{#if data.search}<a href="/database" aria-label="Clear database search">CLEAR</a>{/if}
|
||||
</form>
|
||||
</section>
|
||||
{#if data.search}
|
||||
<section class="database-global-results" aria-live="polite">
|
||||
<header><div><span>SEARCH RESULTS</span><strong>“{data.search}”</strong></div><b>{data.searchResults.length}{data.searchResults.length === 40 ? '+' : ''} MATCHES</b></header>
|
||||
{#if data.searchResults.length}
|
||||
<div>
|
||||
{#each data.searchResults as result}
|
||||
<a href={`/database/${result.slug}?q=${encodeURIComponent(result.name)}`}>
|
||||
<small>{result.category}</small>
|
||||
<strong>{result.name}</strong>
|
||||
<span>{result.detail}</span>
|
||||
<b>VIEW →</b>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<p>No database entries match this search. Try an item name, category, attribute, talent effect, or damage type.</p>
|
||||
{/if}
|
||||
</section>
|
||||
{/if}
|
||||
<section class="database-registry-grid">
|
||||
{#each data.registries as registry, index}
|
||||
{#each data.registries as registry}
|
||||
<a href={`/database/${registry.slug}`}>
|
||||
<header><small>{String(index + 1).padStart(2, '0')} // {registry.code}</small><b>{registry.count}</b></header>
|
||||
<h2>{registry.label}</h2>
|
||||
<header><h2>{registry.label}</h2><b>{registry.count}</b></header>
|
||||
<p>{registry.description}</p>
|
||||
<span>{registry.count ? 'OPEN ACTIVE DATA →' : 'AWAITING CONTRIBUTIONS →'}</span>
|
||||
<footer><small>{registry.count ? `${registry.count} ENTRIES` : 'NO ENTRIES YET'}</small><span>OPEN REGISTRY →</span></footer>
|
||||
</a>
|
||||
{/each}
|
||||
</section>
|
||||
|
||||
@@ -1,31 +1,29 @@
|
||||
<script lang="ts">
|
||||
import DatabaseCategoryNav from '$lib/components/DatabaseCategoryNav.svelte';
|
||||
import DatabaseItemCard from '$lib/components/DatabaseItemCard.svelte';
|
||||
import DatabaseFacetFilters from '$lib/components/DatabaseFacetFilters.svelte';
|
||||
import RegistryPagination from '$lib/components/RegistryPagination.svelte';
|
||||
import DatabaseDiscoveryConsole from '$lib/components/DatabaseDiscoveryConsole.svelte';
|
||||
let { data } = $props();
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{data.category.label} database — SHD.build</title>
|
||||
<title>{data.category.label} — The Division Resurgence database — SHD.build</title>
|
||||
<meta name="description" content={`${data.category.description} Community-maintained data for The Division Resurgence.`}/>
|
||||
</svelte:head>
|
||||
|
||||
<main class="page database-category-page">
|
||||
<header class="database-category-command">
|
||||
<div><p class="eyebrow"><span></span> ISAC PUBLIC ARCHIVE / {data.category.code}</p><h1>{data.category.label}</h1><p>{data.category.description}</p></div>
|
||||
<div><p class="eyebrow"><span></span> ISAC PUBLIC ARCHIVE / DATABASE REGISTRY</p><h1>{data.category.label}</h1><p>{data.category.description}</p></div>
|
||||
<div><strong>{data.pagination.total}</strong><span>ACTIVE RECORDS</span><a href="/database">DATABASE INDEX →</a></div>
|
||||
</header>
|
||||
<DatabaseCategoryNav active={data.category.slug}/>
|
||||
{#if data.filters?.length}<DatabaseFacetFilters groups={data.filters}/>{/if}
|
||||
<RegistryPagination pagination={data.pagination} search={data.search} searchPlaceholder={`SEARCH ${data.category.label.toUpperCase()} BY NAME`}/>
|
||||
<DatabaseDiscoveryConsole filters={data.filters} pagination={data.pagination} search={data.search} searchPlaceholder={`SEARCH ${data.category.label.toUpperCase()} BY NAME`}/>
|
||||
<section class="database-item-grid">
|
||||
{#each data.items as item}
|
||||
<DatabaseItemCard item={item as any}/>
|
||||
{:else}
|
||||
<div class="database-empty">
|
||||
<span>◇</span><strong>{data.search ? 'NO MATCHING RECORDS' : 'NO ACTIVE RECORDS'}</strong>
|
||||
<p>{data.search ? `No ${data.category.label.toLocaleLowerCase()} match “${data.search}”.` : `This registry is awaiting approved ${data.category.label.toLocaleLowerCase()} data.`}</p>
|
||||
<p>{data.search ? `No ${data.category.label.toLocaleLowerCase()} match “${data.search}”.` : `No ${data.category.label.toLocaleLowerCase()} have been added yet.`}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user