Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions PLANS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Keep this checklist aligned with repository behavior. Check off work in the same

## Characters: Available

- [x] Add Brisshell as a Tier 5 Earth Assassin with her catalog images.
- [x] Filter owned and selectable characters by Tier 4 and Tier 5.
- [x] Sort owned characters by name or awakening level in either direction.
- [x] Show owned/total roster progress and disable additions when every character is owned.
Expand Down Expand Up @@ -126,6 +127,7 @@ Implementation is intentionally deferred. Keep every item unchecked until the co

## Chunk 2: Artifacts Inventory

- [x] Add Brisshell's Tier 5 Earth Assassin artifact, Monstrous Longing.
- [x] Define typed artifact data, owned-artifact fields, images, and validation from the game source.
- [x] Add owned-artifact create, edit, delete, reset, local persistence, and mutation timestamps.
- [x] Build the Artifacts page with cards, search, filters, empty states, and accessible forms.
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2,230 changes: 1,049 additions & 1,181 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions src/components/checklist/utils/checklist.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,12 +213,12 @@ describe("checklist schedule utilities", () => {
it("prefers player schedule versions over official completion versions", () => {
const official = {
...daily,
completionVersion: 2,
completionVersion: "1.3.0",
};
expect(occurrenceKey(official, 0)).toBe(
"daily:v2:1970-01-01T00:00:00.000Z",
"daily:v1.3.0:1970-01-01T00:00:00.000Z",
);
expect(fullCompletionKey(official)).toBe("daily:v2:full");
expect(fullCompletionKey(official)).toBe("daily:v1.3.0:full");

const player = {
...official,
Expand Down
8 changes: 4 additions & 4 deletions src/components/monsterlings/store/stat-options-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { persist } from "zustand/middleware";
import type { SelectOption } from "@/constants";
import { STAT_DATA } from "@/data/stats/STAT_DATA";

const STAT_OPTIONS_VERSION = "1.1.0";
const STAT_OPTIONS_GAME_VERSION = "1.3.0";
export const STAT_OPTIONS_CACHE = "stat-options-cache";

const initialState = {
statOptions: [],
version: STAT_OPTIONS_VERSION,
version: STAT_OPTIONS_GAME_VERSION,
};

export type StatOptionsStore = {
Expand All @@ -27,7 +27,7 @@ export const useStatOptionStore = create<StatOptionsStore>()(

if (
state.statOptions.length > 0 &&
state.version === STAT_OPTIONS_VERSION
state.version === STAT_OPTIONS_GAME_VERSION
) {
return state.statOptions;
}
Expand All @@ -36,7 +36,7 @@ export const useStatOptionStore = create<StatOptionsStore>()(

set({
statOptions: options,
version: STAT_OPTIONS_VERSION,
version: STAT_OPTIONS_GAME_VERSION,
});

return options;
Expand Down
29 changes: 23 additions & 6 deletions src/data/artifacts/ARTIFACTS_DATA.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,33 @@ import { existsSync } from "node:fs";
import { resolve } from "node:path";
import { describe, expect, it } from "vitest";
import { ARTIFACTS_DATA } from "@/data/artifacts/ARTIFACTS_DATA";
import { CHARACTER_CLASS_DATA } from "@/data/character-classes/CHARACTER_CLASS_DATA";
import { ELEMENTS_DATA } from "@/data/elements/ELEMENTS_DATA";
import { TIERS_DATA } from "@/data/tiers/TIERS_DATA";
import {
CHARACTER_CLASS_DATA,
CLASS_ID_BY_CLASS,
} from "@/data/character-classes/CHARACTER_CLASS_DATA";
import {
ELEMENT_ID_BY_ELEMENT,
ELEMENTS_DATA,
} from "@/data/elements/ELEMENTS_DATA";
import { TIER_ID_BY_TIER, TIERS_DATA } from "@/data/tiers/TIERS_DATA";

describe("artifacts data", () => {
it("includes Brisshell's signature artifact", () => {
expect(ARTIFACTS_DATA[38]).toEqual({
id: 38,
name: "Monstrous Longing",
image: "/images/Icon_Artifact/ArtifactBrisshell.webp",
tier_id: TIER_ID_BY_TIER.PRIME_5,
class_id: CLASS_ID_BY_CLASS.ASSASSIN,
element_effect_id: ELEMENT_ID_BY_ELEMENT.EARTH,
});
});

it("contains all local artifact images with stable unique ids", () => {
const artifacts = Object.values(ARTIFACTS_DATA);
expect(artifacts).toHaveLength(37);
expect(new Set(artifacts.map((a) => a.id)).size).toBe(37);
expect(new Set(artifacts.map((a) => a.image)).size).toBe(37);
expect(artifacts).toHaveLength(38);
expect(new Set(artifacts.map((a) => a.id)).size).toBe(38);
expect(new Set(artifacts.map((a) => a.image)).size).toBe(38);
for (const artifact of artifacts) {
expect(TIERS_DATA[artifact.tier_id]).toBeDefined();
expect(CHARACTER_CLASS_DATA[artifact.class_id]).toBeDefined();
Expand Down
8 changes: 8 additions & 0 deletions src/data/artifacts/ARTIFACTS_DATA.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,4 +319,12 @@ export const ARTIFACTS_DATA: Record<ArtifactId, Artifact> = {
CLASS_ID_BY_CLASS.FIGHTER,
ELEMENT_ID_BY_ELEMENT.FIRE,
),
38: artifact(
38,
"Monstrous Longing",
"Brisshell",
TIER_ID_BY_TIER.PRIME_5,
CLASS_ID_BY_CLASS.ASSASSIN,
ELEMENT_ID_BY_ELEMENT.EARTH,
),
};
24 changes: 21 additions & 3 deletions src/data/characters/CHARACTERS_DATA.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
import { existsSync } from "node:fs";
import { resolve } from "node:path";
import { describe, expect, it } from "vitest";
import { CHARACTER_CLASS_DATA } from "@/data/character-classes/CHARACTER_CLASS_DATA";
import {
CHARACTER_CLASS_DATA,
CLASS_ID_BY_CLASS,
} from "@/data/character-classes/CHARACTER_CLASS_DATA";
import { CHARACTERS_DATA } from "@/data/characters/CHARACTERS_DATA";
import { ELEMENTS_DATA } from "@/data/elements/ELEMENTS_DATA";
import { TIERS_DATA } from "@/data/tiers/TIERS_DATA";
import {
ELEMENT_ID_BY_ELEMENT,
ELEMENTS_DATA,
} from "@/data/elements/ELEMENTS_DATA";
import { TIER_ID_BY_TIER, TIERS_DATA } from "@/data/tiers/TIERS_DATA";

describe("CHARACTERS_DATA", () => {
it("includes Brisshell with her catalog classifications and images", () => {
expect(CHARACTERS_DATA[24]).toEqual({
id: 24,
name: "Brisshell",
class_id: CLASS_ID_BY_CLASS.ASSASSIN,
element_id: ELEMENT_ID_BY_ELEMENT.EARTH,
portraitImage: "/images/Character_Portrait/portrait_Brisshell_01.webp",
fullImage: "/images/Character_Full/Img_CharacterIllust_Brisshell.webp",
tier_id: TIER_ID_BY_TIER.PRIME_5,
});
});

it("has unique identities and valid references", () => {
const characters = Object.values(CHARACTERS_DATA);
expect(new Set(characters.map(({ id }) => id)).size).toBe(
Expand Down
9 changes: 9 additions & 0 deletions src/data/characters/CHARACTERS_DATA.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,4 +336,13 @@ export const CHARACTERS_DATA: Record<number, Character> = {
fullImage: "/images/Character_Full/Img_CharacterIllust_Maybell.webp",
tier_id: TIER_ID_BY_TIER.PRIME_5,
},
24: {
id: 24,
name: "Brisshell",
class_id: CLASS_ID_BY_CLASS.ASSASSIN,
element_id: ELEMENT_ID_BY_ELEMENT.EARTH,
portraitImage: "/images/Character_Portrait/portrait_Brisshell_01.webp",
fullImage: "/images/Character_Full/Img_CharacterIllust_Brisshell.webp",
tier_id: TIER_ID_BY_TIER.PRIME_5,
},
};
6 changes: 3 additions & 3 deletions src/data/checklist/CHECKLIST_DATA.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, expect, it } from "vitest";
import {
CHECKLIST_KINDS,
CURRENT_SEASON_COMPLETION_VERSION,
CURRENT_SEASON_GAME_VERSION,
PERMANENT_EVENTS,
} from "@/data/checklist/CHECKLIST_DATA";

Expand All @@ -22,12 +22,12 @@ describe("CHECKLIST_DATA", () => {
PERMANENT_EVENTS.map((event) => [event.id, event]),
);
expect(permanentById["dimensional-rift"]).toMatchObject({
completionVersion: CURRENT_SEASON_COMPLETION_VERSION,
completionVersion: CURRENT_SEASON_GAME_VERSION,
recurrence: "weekly",
});
expect(permanentById["monster-race"]).toMatchObject({
startAt: "2026-07-29T01:30:00.000Z",
completionVersion: CURRENT_SEASON_COMPLETION_VERSION,
completionVersion: CURRENT_SEASON_GAME_VERSION,
seasonal: true,
});
expect(permanentById["dimensional-rift"]).not.toHaveProperty("seasonal");
Expand Down
8 changes: 4 additions & 4 deletions src/data/checklist/CHECKLIST_DATA.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const CHECKLIST_MODE_VALUES = [
] as const;

// Shared by Rift and seasonal definitions; increment after official activity refreshes.
export const CURRENT_SEASON_COMPLETION_VERSION = 2;
export const CURRENT_SEASON_GAME_VERSION = "1.3.0";

export type ChecklistDefinition = {
id: string;
Expand All @@ -58,7 +58,7 @@ export type ChecklistDefinition = {
intervalDays?: number;
mode?: ChecklistMode;
dueDurationMinutes?: number;
completionVersion?: number;
completionVersion?: string;
participation?: "discord";
seasonal?: boolean;
};
Expand All @@ -70,14 +70,14 @@ export const PERMANENT_EVENTS: ChecklistDefinition[] = [
kind: CHECKLIST_KINDS.PERMANENT,
startAt: "2024-01-01T00:00:00.000Z",
recurrence: CHECKLIST_RECURRENCES.WEEKLY,
completionVersion: CURRENT_SEASON_COMPLETION_VERSION,
completionVersion: CURRENT_SEASON_GAME_VERSION,
},
{
id: "monster-race",
title: "Monster Race",
kind: CHECKLIST_KINDS.PERMANENT,
startAt: "2026-07-29T01:30:00.000Z",
completionVersion: CURRENT_SEASON_COMPLETION_VERSION,
completionVersion: CURRENT_SEASON_GAME_VERSION,
seasonal: true,
},
{
Expand Down
Loading