diff --git a/apps/web/app/[language]/festival/wintersday/(index)/page.tsx b/apps/web/app/[language]/festival/wintersday/(index)/page.tsx
index ab3b5d84e..2e64f7b04 100644
--- a/apps/web/app/[language]/festival/wintersday/(index)/page.tsx
+++ b/apps/web/app/[language]/festival/wintersday/(index)/page.tsx
@@ -1,4 +1,6 @@
+import { Gw2Accounts } from '@/components/Gw2Api/Gw2Accounts';
import { Trans } from '@/components/I18n/Trans';
+import { ItemInventoryTable } from '@/components/Item/ItemInventoryTable';
import { ItemTable } from '@/components/ItemTable/ItemTable';
import { ItemTableColumnsButton } from '@/components/ItemTable/ItemTableColumnsButton';
import { ItemTableContext } from '@/components/ItemTable/ItemTableContext';
@@ -6,6 +8,14 @@ import { PageLayout } from '@/components/Layout/PageLayout';
import { pageView } from '@/lib/pageView';
import { Headline } from '@gw2treasures/ui/components/Headline/Headline';
import type { Metadata } from 'next';
+import { requiredScopes } from '../helper';
+import { db } from '@/lib/prisma';
+import { linkProperties } from '@/lib/linkProperties';
+import { cache } from '@/lib/cache';
+import { ItemLink } from '@/components/Item/ItemLink';
+import type { PageProps } from '@/lib/next';
+import { getTranslate } from '@/lib/translate';
+import { Fragment } from 'react';
const itemIds = [
86601,
@@ -13,20 +23,51 @@ const itemIds = [
77604,
];
+const loadData = cache(async function loadData() {
+ const [items] = await Promise.all([
+ db.item.findMany({
+ where: { id: { in: itemIds }},
+ select: linkProperties
+ })
+ ]);
+
+ return { items };
+}, ['wintersday-items'], { revalidate: 60 * 60 });
+
+
export default async function WintersdayPage() {
+ const { items } = await loadData();
await pageView('festival/wintersday');
return (
+
} id="items">
+
+ } authorizationMessage={}>
+ {items.map((item) => (
+
+
+
+
+ ))}
+
+
);
}
-export const metadata: Metadata = {
- title: 'Wintersday'
-};
+export async function generateMetadata({ params }: PageProps): Promise {
+ const { language } = await params;
+ const t = getTranslate(language);
+
+ return {
+ title: {
+ absolute: `${t('festival.wintersday')} · gw2treasures.com`
+ }
+ };
+}
diff --git a/apps/web/app/[language]/festival/wintersday/achievements/page.tsx b/apps/web/app/[language]/festival/wintersday/achievements/page.tsx
index 3505ed8cd..7b8c1c736 100644
--- a/apps/web/app/[language]/festival/wintersday/achievements/page.tsx
+++ b/apps/web/app/[language]/festival/wintersday/achievements/page.tsx
@@ -13,6 +13,7 @@ import { Notice } from '@gw2treasures/ui/components/Notice/Notice';
import type { Metadata } from 'next';
import { requiredScopes } from '../helper';
import { pageView } from '@/lib/pageView';
+import { getTranslate } from '@/lib/translate';
const achievementIds = [
5005,
@@ -89,6 +90,12 @@ export default async function WintersdayAchievementsPage({ params }: PageProps)
);
}
-export const metadata: Metadata = {
- title: 'Achievements'
-};
+export async function generateMetadata({ params }: PageProps): Promise {
+ const { language } = await params;
+ const t = getTranslate(language);
+
+ return {
+ title: t('navigation.achievements')
+ };
+}
+
diff --git a/apps/web/app/[language]/festival/wintersday/layout.tsx b/apps/web/app/[language]/festival/wintersday/layout.tsx
index 103d005b3..dbeaa9f60 100644
--- a/apps/web/app/[language]/festival/wintersday/layout.tsx
+++ b/apps/web/app/[language]/festival/wintersday/layout.tsx
@@ -14,13 +14,14 @@ const endsAt = new Date('2025-01-02T17:00:00.000Z');
export default function WintersdayLayout({ children }: LayoutProps) {
return (
- Time remaining: >}>Wintersday)} skipLayout
+ Time remaining: >}>)} skipLayout
navBar={(
},
{ segment: 'achievements', label: },
{ segment: 'skins', label: },
{ segment: 'minis', label: },
+ { segment: 'wizards-vault', label: },
]}/>
)}
>
diff --git a/apps/web/app/[language]/festival/wintersday/minis/page.tsx b/apps/web/app/[language]/festival/wintersday/minis/page.tsx
index d0a50c890..ac3b508d2 100644
--- a/apps/web/app/[language]/festival/wintersday/minis/page.tsx
+++ b/apps/web/app/[language]/festival/wintersday/minis/page.tsx
@@ -8,6 +8,8 @@ import { db } from '@/lib/prisma';
import type { Metadata } from 'next';
import { requiredScopes } from '../helper';
import { pageView } from '@/lib/pageView';
+import { getTranslate } from '@/lib/translate';
+import type { PageProps } from '@/lib/next';
const miniIds = [
115, // Mini Princess Doll
@@ -76,6 +78,11 @@ export default async function WintersdayAchievementsPage() {
);
}
-export const metadata: Metadata = {
- title: 'Minis'
-};
+export async function generateMetadata({ params }: PageProps): Promise {
+ const { language } = await params;
+ const t = getTranslate(language);
+
+ return {
+ title: t('festival.wintersday.minis')
+ };
+}
diff --git a/apps/web/app/[language]/festival/wintersday/skins/page.tsx b/apps/web/app/[language]/festival/wintersday/skins/page.tsx
index 776df9014..060c9841d 100644
--- a/apps/web/app/[language]/festival/wintersday/skins/page.tsx
+++ b/apps/web/app/[language]/festival/wintersday/skins/page.tsx
@@ -8,6 +8,8 @@ import { db } from '@/lib/prisma';
import type { Metadata } from 'next';
import { requiredScopes } from '../helper';
import { pageView } from '@/lib/pageView';
+import type { PageProps } from '@/lib/next';
+import { getTranslate } from '@/lib/translate';
const skinIds = [
// weapons
@@ -103,6 +105,11 @@ export default async function WintersdayAchievementsPage() {
);
}
-export const metadata: Metadata = {
- title: 'Skins'
-};
+export async function generateMetadata({ params }: PageProps): Promise {
+ const { language } = await params;
+ const t = getTranslate(language);
+
+ return {
+ title: t('navigation.skins')
+ };
+}
diff --git a/apps/web/app/[language]/festival/wintersday/wizards-vault/page.tsx b/apps/web/app/[language]/festival/wintersday/wizards-vault/page.tsx
new file mode 100644
index 000000000..86d8aac68
--- /dev/null
+++ b/apps/web/app/[language]/festival/wintersday/wizards-vault/page.tsx
@@ -0,0 +1,64 @@
+import { Gw2Accounts } from '@/components/Gw2Api/Gw2Accounts';
+import { Trans } from '@/components/I18n/Trans';
+import { PageLayout } from '@/components/Layout/PageLayout';
+import { cache } from '@/lib/cache';
+import { db } from '@/lib/prisma';
+import type { Metadata } from 'next';
+import { requiredScopes } from '../helper';
+import { pageView } from '@/lib/pageView';
+import Link from 'next/link';
+import { WizardsVaultObjective } from '@/components/WizardsVault/WizardsVaultObjective';
+import type { PageProps } from '@/lib/next';
+import { getTranslate } from '@/lib/translate';
+import { Icon } from '@gw2treasures/ui';
+import { Notice } from '@gw2treasures/ui/components/Notice/Notice';
+
+const objectiveIds: number[] = [
+ // TODO: add objective ids
+];
+
+const loadData = cache(async function loadData() {
+ const [objectives] = await Promise.all([
+ db.wizardsVaultObjective.findMany({
+ where: { OR: [{ id: { in: objectiveIds }}, { name_en: { startsWith: '(Festival)' }, removedFromApi: false }] },
+ })
+ ]);
+
+ return { objectives };
+}, ['wintersday-wizards-vault-objectives'], { revalidate: 60 * 5 });
+
+
+export default async function WintersdayAchievementsPage({ params }: PageProps) {
+ const { language } = await params;
+ const { objectives } = await loadData();
+ await pageView('festival/wintersday/wizwards-vault');
+
+ return (
+
+ } authorizationMessage={}/>
+
+
+
+ {objectives.length > 0 ? objectives.map((objective) => (
+
+ )) : (
+ No Wizard's Vault objectives for wintersday are available in the Guild Wars 2 API yet.
+ )}
+
+
+ {' '}
+ Visit the Wizard's Vault page to view all your active Wizard's Vault objectives.
+
+
+
+ );
+}
+
+export async function generateMetadata({ params }: PageProps): Promise {
+ const { language } = await params;
+ const t = getTranslate(language);
+
+ return {
+ title: t('navigation.wizardsVault')
+ };
+}
diff --git a/apps/web/components/Gw2Api/Gw2AccountName.tsx b/apps/web/components/Gw2Api/Gw2AccountName.tsx
index 0d97c18b8..fec7d2ab2 100644
--- a/apps/web/components/Gw2Api/Gw2AccountName.tsx
+++ b/apps/web/components/Gw2Api/Gw2AccountName.tsx
@@ -1,6 +1,7 @@
import type { FC } from 'react';
import type { Gw2Account } from './types';
import { Tip } from '@gw2treasures/ui/components/Tip/Tip';
+import commonStyles from '@gw2treasures/ui/common.module.css';
interface Gw2AccountNameProps {
account: Gw2Account;
@@ -10,16 +11,16 @@ interface Gw2AccountNameProps {
export const Gw2AccountName: FC = ({ account, long }) => {
// if the account does not have a displayName, always just return the name
if(!account.displayName) {
- return account.name;
+ return {account.name};
}
if(long) {
- return `${account.displayName} (${account.name})`;
+ return {account.displayName} ({account.name});
}
return (
- {account.displayName}
+ {account.displayName}
);
};
diff --git a/apps/web/components/Gw2Api/Gw2Accounts.tsx b/apps/web/components/Gw2Api/Gw2Accounts.tsx
index 813a4679a..23ccbc860 100644
--- a/apps/web/components/Gw2Api/Gw2Accounts.tsx
+++ b/apps/web/components/Gw2Api/Gw2Accounts.tsx
@@ -11,7 +11,7 @@ import { useUser } from '../User/use-user';
import { Gw2AccountLoginNotice } from './Gw2AccountLoginNotice';
export interface Gw2AccountsProps {
- children?: (accounts: Gw2Account[], scopes: Scope[]) => ReactElement;
+ children?: ((accounts: Gw2Account[], scopes: Scope[]) => ReactElement) | ReactNode;
requiredScopes: Scope[];
optionalScopes?: Scope[];
options?: GetAccountsOptions;
@@ -58,5 +58,9 @@ const Gw2AccountsInternal: FC = ({ children, requiredScopes, o
);
}
- return children?.(accounts.accounts, accounts.scopes);
+ if(typeof children === 'function') {
+ return children?.(accounts.accounts, accounts.scopes);
+ }
+
+ return children;
};
diff --git a/apps/web/components/Item/ItemInventoryTable.tsx b/apps/web/components/Item/ItemInventoryTable.tsx
index 04a0620ba..f60439096 100644
--- a/apps/web/components/Item/ItemInventoryTable.tsx
+++ b/apps/web/components/Item/ItemInventoryTable.tsx
@@ -39,7 +39,7 @@ export const ItemInventoryTable: FC = ({ itemId }) => {
);
};
diff --git a/apps/web/translations/de.json b/apps/web/translations/de.json
index f5b671eaa..99b14797d 100644
--- a/apps/web/translations/de.json
+++ b/apps/web/translations/de.json
@@ -295,5 +295,13 @@
"fractal.uncategorized": "Nicht kategorisiert",
"fractal.underground_facility": "Untergrundeinrichtung",
"fractal.urban_battleground": "Urbanes Schlachtfeld",
- "fractal.volcanic": "Vulkanisches Fraktal"
+ "fractal.volcanic": "Vulkanisches Fraktal",
+ "festival.wintersday": "Wintertag",
+ "festival.wintersday.intro": "Die Glocken klingen, Schneebälle fliegen und das fantastische Golem-Luftschiff von Spielzeugmacher Tixx macht Halt über Götterfels – in Tyria wird einmal mehr das traditionelle Wintertag-Fest gefeiert.",
+ "festival.wintersday.description": "Verfolge während des Wintertags deine Fortschritte auf allen deinen Konten auf gw2treasures.com.",
+ "festival.wintersday.achievements.description": "Diese Erfolge sind nur während des Wintertags verfügbar.",
+ "festival.wintersday.skins.description": "Diese Skins sind nur während des Wintertags verfügbar.",
+ "festival.wintersday.minis": "Miniaturen",
+ "festival.wintersday.minis.description": "Diese Miniaturen sind nur während des Wintertags verfügbar.",
+ "festival.wintersday.wizards-vault.description": "Diese Aufgaben im Gewölbe des Zauberers sind nur während dest Wintertags verfügbar."
}
diff --git a/apps/web/translations/en.json b/apps/web/translations/en.json
index 76f6a21a8..8aa310d08 100644
--- a/apps/web/translations/en.json
+++ b/apps/web/translations/en.json
@@ -307,7 +307,10 @@
"fractal.urban_battleground": "Urban Battleground",
"fractal.volcanic": "Volcanic",
"festival.wintersday": "Wintersday",
- "festival.wintersday.description": "Bells are ringing, snowballs are flying, and Toymaster Tixx’s fantastic golem-shaped airship is hovering over Divinity’s Reach—the traditional Tyrian celebration of Wintersday is here again!",
+ "festival.wintersday.intro": "Bells are ringing, snowballs are flying, and Toymaster Tixx’s fantastic golem-shaped airship is hovering over Divinity’s Reach—the traditional Tyrian celebration of Wintersday is here again!",
+ "festival.wintersday.description": "Keep track of your progress on all your accounts during Wintersday on gw2treasures.com.",
+ "festival.wintersday.items.authorize": "You need to authorize gw2treasures.com to be able to see your accounts inventories.",
+ "festival.wintersday.items.login": "You need to login to be able to see your accounts inventories.",
"festival.wintersday.achievements.description": "These achievements are only available during wintersday.",
"festival.wintersday.achievements.authorize": "You need to authorize gw2treasures.com to be able to see your accounts achievement progress.",
"festival.wintersday.achievements.login": "You need to login to be able to see your accounts achievement progress.",
@@ -317,5 +320,8 @@
"festival.wintersday.minis": "Minis",
"festival.wintersday.minis.description": "These minis are only available during wintersday.",
"festival.wintersday.minis.authorize": "You need to authorize gw2treasures.com to be able to see your accounts mini unlocks.",
- "festival.wintersday.minis.login": "You need to login to be able to see your accounts mini unlocks."
+ "festival.wintersday.minis.login": "You need to login to be able to see your accounts mini unlocks.",
+ "festival.wintersday.wizards-vault.description": "These are all the Wizard's Vault objectives only active during Wintersday.",
+ "festival.wintersday.wizards-vault.authorize": "You need to authorize gw2treasures.com to be able to see your accounts Wizard's Vault progress.",
+ "festival.wintersday.wizards-vault.login": "You need to login to be able to see your accounts Wizard's Vault progress."
}
diff --git a/packages/ui/common.module.css b/packages/ui/common.module.css
new file mode 100644
index 000000000..101ddcf4c
--- /dev/null
+++ b/packages/ui/common.module.css
@@ -0,0 +1,3 @@
+.nowrap {
+ white-space: nowrap;
+}
diff --git a/packages/ui/index.ts b/packages/ui/index.ts
index 9fb20bbf5..55f7f0760 100644
--- a/packages/ui/index.ts
+++ b/packages/ui/index.ts
@@ -5,3 +5,6 @@ export * from './icons';
// lib
export * from './lib';
+
+import commonStyles from './common.module.css';
+export { commonStyles };
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 744b271a4..63b68b93d 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -12,6 +12,7 @@
"icons/",
"lib/",
"reset.module.css",
+ "common.module.css",
"index.ts",
"types.d.ts",
"tsconfig.json",
diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json
index 0eecfe634..092f92dbf 100644
--- a/packages/ui/tsconfig.json
+++ b/packages/ui/tsconfig.json
@@ -4,6 +4,7 @@
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
+ "**/*.module.css",
".next/types/**/*.ts"
],
}