feat (www): Add Wanted (wishlist) page (#15)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
1 parent
07b16d404c
commit
ef6ca40c72
29 files changed
+2232
-225
No files matched your search
@@ -29,6 +29,7 @@
|
||||
"@ermnvldmr/i18n": "workspace:*",
|
||||
"@ermnvldmr/stl": "workspace:*",
|
||||
"@ermnvldmr/ui": "workspace:*",
|
||||
"lucide-react": "^0.503.0",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"tailwindcss": "^4.1.4"
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
import { createKeyset } from '@ermnvldmr/i18n';
|
||||
|
||||
export const { t } = createKeyset({
|
||||
Wanted: {
|
||||
en: 'Wanted',
|
||||
ru: 'Разыскивается',
|
||||
},
|
||||
Home: {
|
||||
en: 'Home',
|
||||
ru: 'Главная'
|
||||
},
|
||||
'A quiet log of things I am planning to pick up eventually. It serves mostly as a personal reference, but also as a zero-pressure guide for friends.': {
|
||||
en: 'A quiet log of things I am planning to pick up eventually. It serves mostly as a personal reference, but also as a zero-pressure guide for friends.',
|
||||
ru: 'Список вещей, которые я планирую со временем приобрести. В основном это личная заметка, но также и ненавязчивый вишлист.',
|
||||
},
|
||||
'How this works': {
|
||||
en: 'How this works',
|
||||
ru: 'Как это работает',
|
||||
},
|
||||
'Think of this as a living list of things I plan to acquire over time. If you ever feel inspired to contribute, I would be genuinely thrilled, but please know it is never expected.': {
|
||||
en: 'Think of this as a living list of things I plan to acquire over time. If you ever feel inspired to contribute, I would be genuinely thrilled, but please know it is never expected.',
|
||||
ru: 'Считайте это живым списком вещей, которые я планирую приобрести. Если у вас когда-нибудь появится желание поучаствовать — я буду искренне рад, но, пожалуйста, знайте, что это абсолютно необязательно.',
|
||||
},
|
||||
'When it comes to the items themselves, authenticity and quality matter a lot to me. I would always prefer to have nothing at all rather than a replica or a knock-off.': {
|
||||
en: 'When it comes to the items themselves, authenticity and quality matter a lot to me. I would always prefer to have nothing at all rather than a replica or a knock-off.',
|
||||
ru: 'Когда дело доходит до самих вещей, подлинность и качество значат для меня очень много. Я всегда предпочту не иметь ничего, чем реплику или подделку.',
|
||||
},
|
||||
'And of course, if you are ever unsure about a specific edition or detail, we can always just talk about it.': {
|
||||
en: 'And of course, if you are ever unsure about a specific edition or detail, we can always just talk about it.',
|
||||
ru: 'И, конечно, если вы не уверены в каком-то конкретном издании или деталях, можно всегда это обсудить.',
|
||||
},
|
||||
'Figurines': {
|
||||
en: 'Figurines',
|
||||
ru: 'Фигурки',
|
||||
},
|
||||
'The specifics of the edition and its authenticity are quite important here. It is usually best to know the exact details to get it right.': {
|
||||
en: 'The specifics of the edition and its authenticity are quite important here. It is usually best to know the exact details to get it right.',
|
||||
ru: 'Специфика издания и его подлинность здесь довольно важны. Обычно лучше знать точные детали, чтобы не ошибиться.',
|
||||
},
|
||||
'Looking for': {
|
||||
en: 'Looking for',
|
||||
ru: 'Ищу',
|
||||
},
|
||||
'NENDOROID 1970 Jill Stingray (VA-11 Hall-A)': {
|
||||
en: 'NENDOROID 1970 Jill Stingray (VA-11 Hall-A)',
|
||||
ru: 'NENDOROID 1970 Jill Stingray (VA-11 Hall-A)',
|
||||
},
|
||||
'Mirror’s Edge Catalyst (Collector’s Edition)': {
|
||||
en: 'Mirror’s Edge Catalyst (Collector’s Edition)',
|
||||
ru: 'Mirror’s Edge Catalyst (Collector’s Edition)',
|
||||
},
|
||||
"Deus Ex: Mankind Divided (Collector's Edition)": {
|
||||
en: "Deus Ex: Mankind Divided (Collector's Edition)",
|
||||
ru: "Deus Ex: Mankind Divided (Collector's Edition)",
|
||||
},
|
||||
'Genshin Impact Pop! Aether (#160)': {
|
||||
en: 'Genshin Impact Pop! Aether (#160)',
|
||||
ru: 'Genshin Impact Pop! Aether (#160)',
|
||||
},
|
||||
'LEGO Sets': {
|
||||
en: 'LEGO Sets',
|
||||
ru: 'Наборы LEGO',
|
||||
},
|
||||
'I appreciate sets that focus on engineering and make for good display pieces, like Architecture, Technic, or the Botanical collection. I generally lean away from traditional play-sets.': {
|
||||
en: 'I appreciate sets that focus on engineering and make for good display pieces, like Architecture, Technic, or the Botanical collection. I generally lean away from traditional play-sets.',
|
||||
ru: 'Я ценю наборы, ориентированные на инженерию, которые хорошо смотрятся на полке — например, Architecture, Technic или Botanical. Обычно я избегаю традиционных игровых наборов (friends, city, и другие подобные).',
|
||||
},
|
||||
'Architectural landmarks or skylines': {
|
||||
en: 'Architectural landmarks or skylines',
|
||||
ru: 'Архитектурные наборы',
|
||||
},
|
||||
'NASA and space exploration sets (like the #92176 Ideas NASA Apollo Saturn V)': {
|
||||
en: 'NASA and space exploration sets (like the #92176 Ideas NASA Apollo Saturn V)',
|
||||
ru: 'Наборы NASA и иные аэрокосмические (например, #92176 Ideas NASA Apollo Saturn V)',
|
||||
},
|
||||
'High-fidelity Technic vehicles': {
|
||||
en: 'High-fidelity Technic vehicles',
|
||||
ru: 'Technic и другие высокотехнические наборы',
|
||||
},
|
||||
'Already Owned': {
|
||||
en: 'Already Owned',
|
||||
ru: 'Уже есть',
|
||||
},
|
||||
'Japanese Red Maple Bonsai (10348)': {
|
||||
en: 'Japanese Red Maple Bonsai (10348)',
|
||||
ru: 'Japanese Red Maple Bonsai (10348)',
|
||||
},
|
||||
'The Pig House (21170)': {
|
||||
en: 'The Pig House (21170)',
|
||||
ru: 'The Pig House (21170)',
|
||||
},
|
||||
'Die-cast Models': {
|
||||
en: 'Die-cast Models',
|
||||
ru: 'Дайкаст модели',
|
||||
},
|
||||
'I enjoy high-quality pre-assembled die-cast models. I am not much into plastic kits that require glue or paint to assemble.': {
|
||||
en: 'I enjoy high-quality pre-assembled die-cast models. I am not much into plastic kits that require glue or paint to assemble.',
|
||||
ru: 'Мне нравятся качественные предварительно собранные дайкаст модели. Я не особо увлекаюсь пластиковыми наборами, которые нужно собирать с помощью клея и красок.',
|
||||
},
|
||||
'Spacecraft scale replicas (SpaceX, NASA)': {
|
||||
en: 'Spacecraft scale replicas (SpaceX, NASA)',
|
||||
ru: 'Масштабные модели космических аппаратов (SpaceX, NASA)',
|
||||
},
|
||||
'Motorsport cars (Modern F1, GT3, Endurance Legends)': {
|
||||
en: 'Motorsport cars (Modern F1, GT3, Endurance Legends)',
|
||||
ru: 'Автоспорт (современные болиды Формулы-1, GT3, легенды эндуранс)',
|
||||
},
|
||||
'Aviation models (SR-71 Blackbird, Concorde)': {
|
||||
en: 'Aviation models (SR-71 Blackbird, Concorde)',
|
||||
ru: 'Авиационные модели (SR-71 Blackbird, Concorde)',
|
||||
},
|
||||
'Bburago – McLaren F1 MCL39 2025 #4 Norris': {
|
||||
en: 'Bburago – McLaren F1 MCL39 2025 #4 Norris',
|
||||
ru: 'Bburago – McLaren F1 MCL39 2025 #4 Norris',
|
||||
},
|
||||
'Coffee gear': {
|
||||
en: 'Coffee gear',
|
||||
ru: 'Всё для кофе',
|
||||
},
|
||||
'My current espresso setup uses a 51mm portafilter, so any accessories for it need to match that size perfectly.': {
|
||||
en: 'My current espresso setup uses a 51mm portafilter, so any accessories for it need to match that size perfectly.',
|
||||
ru: 'Мой текущий сетап для эспрессо использует 51-миллиметровый портафильтр, поэтому любые аксессуары для него должны идеально подходить по размеру.',
|
||||
},
|
||||
'AeroPress': {
|
||||
en: 'AeroPress',
|
||||
ru: 'AeroPress',
|
||||
},
|
||||
'Dedicated cold brewer (Hario or Toddy)': {
|
||||
en: 'Dedicated cold brewer (Hario or Toddy)',
|
||||
ru: 'Специализированная колба для колд-брю (Hario или Toddy)',
|
||||
},
|
||||
'51mm WDT Tool': {
|
||||
en: '51mm WDT Tool',
|
||||
ru: 'WDT-инструмент на 51 мм',
|
||||
},
|
||||
'Freshly roasted specialty coffee beans': {
|
||||
en: 'Freshly roasted specialty coffee beans',
|
||||
ru: 'Свежеобжаренные спешелти зерна',
|
||||
},
|
||||
'Espresso Machine': {
|
||||
en: 'Espresso Machine',
|
||||
ru: 'Эспрессо-машина',
|
||||
},
|
||||
'Hario V60': {
|
||||
en: 'Hario V60',
|
||||
ru: 'Hario V60',
|
||||
},
|
||||
'Manual Hand Grinder': {
|
||||
en: 'Manual Hand Grinder',
|
||||
ru: 'Ручная кофемолка',
|
||||
},
|
||||
'French Press': {
|
||||
en: 'French Press',
|
||||
ru: 'Френч-пресс',
|
||||
},
|
||||
'Hobby': {
|
||||
en: 'Hobby',
|
||||
ru: 'Хобби',
|
||||
},
|
||||
'Various tools and equipment for electronics and other side projects.': {
|
||||
en: 'Various tools and equipment for electronics and other side projects.',
|
||||
ru: 'Различные инструменты и оборудование для электроники и других проектов.',
|
||||
},
|
||||
'Alientek T90 Soldering Iron (or a similar compact, smart iron)': {
|
||||
en: 'Alientek T90 Soldering Iron (or a similar compact, smart iron)',
|
||||
ru: 'Паяльник Alientek T90 (или аналогичный компактный умный паяльник)',
|
||||
},
|
||||
'Other items': {
|
||||
en: 'Other items',
|
||||
ru: 'Другое',
|
||||
},
|
||||
'A few standalone pieces that caught my eye.': {
|
||||
en: 'A few standalone pieces that caught my eye.',
|
||||
ru: 'Несколько отдельных вещей, которые привлекли моё внимание.',
|
||||
},
|
||||
'TimePiece® 3D tear-off calendar': {
|
||||
en: 'TimePiece® 3D tear-off calendar',
|
||||
ru: 'Отрывной 3D-календарь TimePiece®',
|
||||
},
|
||||
'Seiko SSC813 Prospex watch': {
|
||||
en: 'Seiko SSC813 Prospex watch',
|
||||
ru: 'Часы Seiko SSC813 Prospex',
|
||||
},
|
||||
'Tag Heuer Carrera watch': {
|
||||
en: 'Tag Heuer Carrera watch',
|
||||
ru: 'Часы Tag Heuer Carrera',
|
||||
},
|
||||
'Cast Iron Cube 10x10x10cm (just for the tactile density)': {
|
||||
en: 'Cast Iron Cube 10x10x10cm (just for the tactile density)',
|
||||
ru: 'Чугунный куб 10x10x10 см (по приколу)',
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,233 @@
|
||||
import { localePath } from '@ermnvldmr/i18n';
|
||||
import { BentoGrid, Header, Paragraph, VStack, Breadcrumbs } from '@ermnvldmr/ui';
|
||||
import { Ghost, Coffee, Plane, Check, Gift, Cuboid, BoxSelect, Cpu } from 'lucide-react';
|
||||
|
||||
import { t } from './index.i18n';
|
||||
import { DefaultLayout } from '../../components/Layout/DefaultLayout';
|
||||
import { createPage } from '../../lib/core/createPage';
|
||||
|
||||
/**
|
||||
* Wanted page component.
|
||||
* Serves as a personal log of things to acquire.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* createPage(WantedPage, { title: t('Wanted'), description: t('A quiet log of things I am planning to pick up eventually. It serves mostly as a personal reference, but also as a zero-pressure guide for friends.') });
|
||||
* ```
|
||||
*/
|
||||
function WantedPage(): React.JSX.Element {
|
||||
const breadcrumbs = (
|
||||
<Breadcrumbs>
|
||||
<Breadcrumbs.Item href={localePath('/')}>{t('Home')}</Breadcrumbs.Item>
|
||||
<Breadcrumbs.Separator />
|
||||
<Breadcrumbs.Item isCurrent>{t('Wanted')}</Breadcrumbs.Item>
|
||||
</Breadcrumbs>
|
||||
);
|
||||
|
||||
return (
|
||||
<DefaultLayout
|
||||
breadcrumbs={breadcrumbs}
|
||||
currentPath={localePath('/wanted')}
|
||||
description={t(
|
||||
'A quiet log of things I am planning to pick up eventually. It serves mostly as a personal reference, but also as a zero-pressure guide for friends.'
|
||||
)}
|
||||
title={t('Wanted')}
|
||||
>
|
||||
<VStack gap={8}>
|
||||
<VStack gap={4}>
|
||||
<Header level={3}>{t('How this works')}</Header>
|
||||
<VStack gap={4}>
|
||||
<Paragraph size="m">
|
||||
{t(
|
||||
'Think of this as a living list of things I plan to acquire over time. If you ever feel inspired to contribute, I would be genuinely thrilled, but please know it is never expected.'
|
||||
)}
|
||||
</Paragraph>
|
||||
<Paragraph size="m">
|
||||
{t(
|
||||
'When it comes to the items themselves, authenticity and quality matter a lot to me. I would always prefer to have nothing at all rather than a replica or a knock-off.'
|
||||
)}
|
||||
</Paragraph>
|
||||
<Paragraph size="m">
|
||||
{t(
|
||||
'And of course, if you are ever unsure about a specific edition or detail, we can always just talk about it.'
|
||||
)}
|
||||
</Paragraph>
|
||||
</VStack>
|
||||
</VStack>
|
||||
|
||||
<BentoGrid>
|
||||
{/* Collectibles - Figurines */}
|
||||
<BentoGrid.ListCard
|
||||
colSpan={2}
|
||||
defaultValue={['wanted']}
|
||||
description={t(
|
||||
'The specifics of the edition and its authenticity are quite important here. It is usually best to know the exact details to get it right.'
|
||||
)}
|
||||
icon={<Ghost size={24} />}
|
||||
sections={[
|
||||
{
|
||||
value: 'wanted',
|
||||
label: t('Looking for'),
|
||||
labelIcon: <Gift size={18} />,
|
||||
items: [
|
||||
t('NENDOROID 1970 Jill Stingray (VA-11 Hall-A)'),
|
||||
t('Mirror’s Edge Catalyst (Collector’s Edition)'),
|
||||
t("Deus Ex: Mankind Divided (Collector's Edition)"),
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 'owned',
|
||||
label: t('Already Owned'),
|
||||
labelIcon: <Check size={18} />,
|
||||
items: [t('Genshin Impact Pop! Aether (#160)')],
|
||||
},
|
||||
]}
|
||||
title={t('Figurines')}
|
||||
/>
|
||||
|
||||
{/* Collectibles - LEGO */}
|
||||
<BentoGrid.ListCard
|
||||
colSpan={2}
|
||||
defaultValue={['wanted']}
|
||||
description={t(
|
||||
'I appreciate sets that focus on engineering and make for good display pieces, like Architecture, Technic, or the Botanical collection. I generally lean away from traditional play-sets.'
|
||||
)}
|
||||
icon={<Cuboid size={24} />}
|
||||
sections={[
|
||||
{
|
||||
value: 'wanted',
|
||||
label: t('Looking for'),
|
||||
labelIcon: <Gift size={18} />,
|
||||
items: [
|
||||
t('Architectural landmarks or skylines'),
|
||||
t('NASA and space exploration sets (like the #92176 Ideas NASA Apollo Saturn V)'),
|
||||
t('High-fidelity Technic vehicles'),
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 'owned',
|
||||
label: t('Already Owned'),
|
||||
labelIcon: <Check size={18} />,
|
||||
items: [t('Japanese Red Maple Bonsai (10348)'), t('The Pig House (21170)')],
|
||||
},
|
||||
]}
|
||||
title={t('LEGO Sets')}
|
||||
/>
|
||||
|
||||
{/* Collectibles - Die-cast */}
|
||||
<BentoGrid.ListCard
|
||||
colSpan={2}
|
||||
defaultValue={['wanted']}
|
||||
description={t(
|
||||
'I enjoy high-quality pre-assembled die-cast models. I am not much into plastic kits that require glue or paint to assemble.'
|
||||
)}
|
||||
icon={<Plane size={24} />}
|
||||
sections={[
|
||||
{
|
||||
value: 'wanted',
|
||||
label: t('Looking for'),
|
||||
labelIcon: <Gift size={18} />,
|
||||
items: [
|
||||
t('Spacecraft scale replicas (SpaceX, NASA)'),
|
||||
t('Motorsport cars (Modern F1, GT3, Endurance Legends)'),
|
||||
t('Aviation models (SR-71 Blackbird, Concorde)'),
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 'owned',
|
||||
label: t('Already Owned'),
|
||||
labelIcon: <Check size={18} />,
|
||||
items: [t('Bburago – McLaren F1 MCL39 2025 #4 Norris')],
|
||||
},
|
||||
]}
|
||||
title={t('Die-cast Models')}
|
||||
/>
|
||||
|
||||
{/* Coffee stuff */}
|
||||
<BentoGrid.ListCard
|
||||
colSpan={2}
|
||||
defaultValue={['wanted']}
|
||||
description={t(
|
||||
'My current espresso setup uses a 51mm portafilter, so any accessories for it need to match that size perfectly.'
|
||||
)}
|
||||
icon={<Coffee size={24} />}
|
||||
sections={[
|
||||
{
|
||||
value: 'wanted',
|
||||
label: t('Looking for'),
|
||||
labelIcon: <Gift size={18} />,
|
||||
items: [
|
||||
t('AeroPress'),
|
||||
t('Dedicated cold brewer (Hario or Toddy)'),
|
||||
t('51mm WDT Tool'),
|
||||
t('Freshly roasted specialty coffee beans'),
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 'owned',
|
||||
label: t('Already Owned'),
|
||||
labelIcon: <Check size={18} />,
|
||||
items: [
|
||||
t('Espresso Machine'),
|
||||
t('Hario V60'),
|
||||
t('Manual Hand Grinder'),
|
||||
t('French Press'),
|
||||
],
|
||||
},
|
||||
]}
|
||||
title={t('Coffee gear')}
|
||||
/>
|
||||
|
||||
{/* Hobby */}
|
||||
<BentoGrid.ListCard
|
||||
colSpan={2}
|
||||
defaultValue={['wanted']}
|
||||
description={t('Various tools and equipment for electronics and other side projects.')}
|
||||
icon={<Cpu size={24} />}
|
||||
sections={[
|
||||
{
|
||||
value: 'wanted',
|
||||
label: t('Looking for'),
|
||||
labelIcon: <Gift size={18} />,
|
||||
items: [
|
||||
t('Alientek T90 Soldering Iron (or a similar compact, smart iron)'),
|
||||
],
|
||||
},
|
||||
]}
|
||||
title={t('Hobby')}
|
||||
/>
|
||||
|
||||
{/* Other stuff */}
|
||||
<BentoGrid.ListCard
|
||||
colSpan={2}
|
||||
defaultValue={['wanted']}
|
||||
description={t('A few standalone pieces that caught my eye.')}
|
||||
icon={<BoxSelect size={24} />}
|
||||
sections={[
|
||||
{
|
||||
value: 'wanted',
|
||||
label: t('Looking for'),
|
||||
labelIcon: <Gift size={18} />,
|
||||
items: [
|
||||
t('TimePiece® 3D tear-off calendar'),
|
||||
t('Seiko SSC813 Prospex watch'),
|
||||
t('Tag Heuer Carrera watch'),
|
||||
t('Cast Iron Cube 10x10x10cm (just for the tactile density)'),
|
||||
],
|
||||
},
|
||||
]}
|
||||
title={t('Other items')}
|
||||
/>
|
||||
</BentoGrid>
|
||||
</VStack>
|
||||
</DefaultLayout>
|
||||
);
|
||||
}
|
||||
|
||||
createPage(WantedPage, {
|
||||
title: t('Wanted'),
|
||||
description: t(
|
||||
'A quiet log of things I am planning to pick up eventually. It serves mostly as a personal reference, but also as a zero-pressure guide for friends.'
|
||||
),
|
||||
});
|
||||
export default WantedPage;
|
||||
@@ -2,5 +2,6 @@ import { createKeyset } from '@ermnvldmr/i18n';
|
||||
|
||||
export const { t } = createKeyset({
|
||||
Articles: { en: 'Articles', ru: 'Статьи' },
|
||||
Wanted: { en: 'Wanted', ru: 'Разыскивается' },
|
||||
GitHub: { en: 'GitHub', ru: 'ГитХаб' },
|
||||
});
|
||||
@@ -37,6 +37,7 @@ export const PageHead = ({ heading, caption, breadcrumbs, metadata, strategy }:
|
||||
const nav = (
|
||||
<HStack gap={4}>
|
||||
<Link href="/articles">{t('Articles')}</Link>
|
||||
<Link href="/wanted">{t('Wanted')}</Link>
|
||||
<Link href="https://github.com/deytenit">{t('GitHub')}</Link>
|
||||
</HStack>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user