diff --git a/src/lib/features/database/jobs/sections/JobFeaturesSection.svelte b/src/lib/features/database/jobs/sections/JobFeaturesSection.svelte
new file mode 100644
index 00000000..185cea2a
--- /dev/null
+++ b/src/lib/features/database/jobs/sections/JobFeaturesSection.svelte
@@ -0,0 +1,90 @@
+
+
+
+
+
+ {#if editMode}
+
+
+
+
+
+
+
+
+
+ {#if editData.accessory}
+
+
+
+ {/if}
+ {:else}
+
+
+ {formatBoolean(job.masterLevel)}
+
+
+
+
+ {formatBoolean(job.ultimateMastery)}
+
+
+
+
+ {formatBoolean(job.accessory)}
+
+
+ {#if job.accessory}
+
+ {/if}
+ {/if}
+
+
+
diff --git a/src/lib/features/database/jobs/sections/JobMetadataSection.svelte b/src/lib/features/database/jobs/sections/JobMetadataSection.svelte
new file mode 100644
index 00000000..42e6f2c1
--- /dev/null
+++ b/src/lib/features/database/jobs/sections/JobMetadataSection.svelte
@@ -0,0 +1,77 @@
+
+
+
+
+
+ {#if editMode}
+
+
+
+
+
+
+
+ {:else}
+
+
+
+ {#if job.granblueId}
+
+ {:else}
+ —
+ {/if}
+
+
+
+ {/if}
+
diff --git a/src/lib/features/database/jobs/sections/JobProficiencySection.svelte b/src/lib/features/database/jobs/sections/JobProficiencySection.svelte
new file mode 100644
index 00000000..5f66201e
--- /dev/null
+++ b/src/lib/features/database/jobs/sections/JobProficiencySection.svelte
@@ -0,0 +1,77 @@
+
+
+
+
+
+ {#if editMode}
+
+
+
+
+
+
+ {:else}
+
+ {#if proficiencyNames[0]}
+
+ {:else}
+ —
+ {/if}
+
+
+ {#if proficiencyNames[1]}
+
+ {:else}
+ —
+ {/if}
+
+ {/if}
+
+
+
diff --git a/src/lib/features/database/jobs/tabs/JobImagesTab.svelte b/src/lib/features/database/jobs/tabs/JobImagesTab.svelte
new file mode 100644
index 00000000..552a17b7
--- /dev/null
+++ b/src/lib/features/database/jobs/tabs/JobImagesTab.svelte
@@ -0,0 +1,163 @@
+
+
+
+
+
+ {#if skillsQuery.isLoading}
+
Loading skills...
+ {:else if skillsQuery.isError}
+
Failed to load skills
+ {:else if !skillsQuery.data?.length}
+
No skills found for this job
+ {:else}
+ {#if hasSkills('main')}
+
+ Main Skills
+
+ {#each groupedSkills.main as skill}
+
+
})
+
+ {skill.name.en}
+ {#if skill.name.ja}
+ {skill.name.ja}
+ {/if}
+
+
+ {getSkillCategoryName(skill)}
+
+
+ {/each}
+
+
+ {/if}
+
+ {#if hasSkills('sub')}
+
+ Subskills
+
+ {#each groupedSkills.sub as skill}
+
+
})
+
+ {skill.name.en}
+ {#if skill.name.ja}
+ {skill.name.ja}
+ {/if}
+
+
+ {getSkillCategoryName(skill)}
+
+
+ {/each}
+
+
+ {/if}
+
+ {#if hasSkills('emp')}
+
+ EMP Skills
+
+ {#each groupedSkills.emp as skill}
+
+
})
+
+ {skill.name.en}
+ {#if skill.name.ja}
+ {skill.name.ja}
+ {/if}
+
+
+ {getSkillCategoryName(skill)}
+
+
+ {/each}
+
+
+ {/if}
+
+ {#if hasSkills('base')}
+
+ Base Skills
+
+ {#each groupedSkills.base as skill}
+
+
})
+
+ {skill.name.en}
+ {#if skill.name.ja}
+ {skill.name.ja}
+ {/if}
+
+
+ {getSkillCategoryName(skill)}
+
+
+ {/each}
+
+
+ {/if}
+ {/if}
+
+
+
diff --git a/src/routes/(app)/database/jobs/+page.svelte b/src/routes/(app)/database/jobs/+page.svelte
new file mode 100644
index 00000000..51385ac9
--- /dev/null
+++ b/src/routes/(app)/database/jobs/+page.svelte
@@ -0,0 +1,320 @@
+
+
+
+
+
+
+ {#if jobsQuery.isLoading}
+
Loading jobs...
+ {:else if jobsQuery.isError}
+
Failed to load jobs
+ {:else}
+
+
+
+
+ | Image |
+ Name |
+ Row |
+ Proficiencies |
+ Features |
+
+
+
+ {#each sortedJobs as job (job.id)}
+ handleRowClick(job)} class="clickable">
+
+
+ |
+
+
+ {job.name.en}
+
+ |
+
+ {getJobTierName(job.row)}
+ |
+
+
+ {#if job.proficiency?.[0]}
+
+ {/if}
+ {#if job.proficiency?.[1]}
+
+ {/if}
+
+ |
+
+
+ {#if job.masterLevel}
+ Master
+ {/if}
+ {#if job.ultimateMastery}
+ Ultimate
+ {/if}
+ {#if job.accessory}
+ Accessory
+ {/if}
+
+ |
+
+ {/each}
+
+
+
+
+
+ {/if}
+
+
+
+
diff --git a/src/routes/(app)/database/jobs/[granblueId]/+page.server.ts b/src/routes/(app)/database/jobs/[granblueId]/+page.server.ts
new file mode 100644
index 00000000..4931a19a
--- /dev/null
+++ b/src/routes/(app)/database/jobs/[granblueId]/+page.server.ts
@@ -0,0 +1,29 @@
+import type { PageServerLoad } from './$types'
+import { jobAdapter } from '$lib/api/adapters/job.adapter'
+import { error } from '@sveltejs/kit'
+
+export const load: PageServerLoad = async ({ params, parent }) => {
+ try {
+ // Get parent data to access role
+ const parentData = await parent()
+
+ const job = await jobAdapter.getById(params.granblueId)
+
+ if (!job) {
+ throw error(404, 'Job not found')
+ }
+
+ return {
+ job,
+ role: parentData.role
+ }
+ } catch (err) {
+ console.error('Failed to load job:', err)
+
+ if (err instanceof Error && 'status' in err && err.status === 404) {
+ throw error(404, 'Job not found')
+ }
+
+ throw error(500, 'Failed to load job')
+ }
+}
diff --git a/src/routes/(app)/database/jobs/[granblueId]/+page.svelte b/src/routes/(app)/database/jobs/[granblueId]/+page.svelte
new file mode 100644
index 00000000..bbb768f8
--- /dev/null
+++ b/src/routes/(app)/database/jobs/[granblueId]/+page.svelte
@@ -0,0 +1,154 @@
+
+ {#if job}
+
+
+
+ Info
+ Skills
+ Images
+
+
+
+ {#if currentTab === 'info'}
+
+ {:else if currentTab === 'skills'}
+
+ {:else if currentTab === 'images'}
+
+ {/if}
+
+ {:else}
+
+
Job Not Found
+
The job you're looking for could not be found.
+
+
+ {/if}
+
+
+
diff --git a/src/routes/(app)/database/jobs/[granblueId]/edit/+page.server.ts b/src/routes/(app)/database/jobs/[granblueId]/edit/+page.server.ts
new file mode 100644
index 00000000..fcac1826
--- /dev/null
+++ b/src/routes/(app)/database/jobs/[granblueId]/edit/+page.server.ts
@@ -0,0 +1,34 @@
+import type { PageServerLoad } from './$types'
+import { jobAdapter } from '$lib/api/adapters/job.adapter'
+import { error, redirect } from '@sveltejs/kit'
+
+export const load: PageServerLoad = async ({ params, parent }) => {
+ // Get parent data to access role
+ const parentData = await parent()
+
+ // Role check - must be editor level (>= 7) to edit
+ if (!parentData.role || parentData.role < 7) {
+ throw redirect(303, `/database/jobs/${params.granblueId}`)
+ }
+
+ try {
+ const job = await jobAdapter.getById(params.granblueId)
+
+ if (!job) {
+ throw error(404, 'Job not found')
+ }
+
+ return {
+ job,
+ role: parentData.role
+ }
+ } catch (err) {
+ console.error('Failed to load job:', err)
+
+ if (err instanceof Error && 'status' in err && err.status === 404) {
+ throw error(404, 'Job not found')
+ }
+
+ throw error(500, 'Failed to load job')
+ }
+}
diff --git a/src/routes/(app)/database/jobs/[granblueId]/edit/+page.svelte b/src/routes/(app)/database/jobs/[granblueId]/edit/+page.svelte
new file mode 100644
index 00000000..54f1e84e
--- /dev/null
+++ b/src/routes/(app)/database/jobs/[granblueId]/edit/+page.svelte
@@ -0,0 +1,201 @@
+
+ {#if job}
+
+
+
+ {:else}
+
+
Job Not Found
+
The job you're looking for could not be found.
+
+
+ {/if}
+
+
+