feat: Abfüllungs-Archiv und Social-Media-Links auf der Herkunftsseite
Abfüllungen hatten keine Übersichtsseite - die Navigation ging direkt auf /bottling/new, sodass eine angelegte Abfüllung danach nirgends mehr sichtbar war. Neue Seite /bottling listet alle Abfüllungen (Archiv), Klick führt zur bestehenden Detail-/Bearbeiten-Seite. Navigationslink zeigt jetzt auf die Liste statt direkt auf "Neu". Neues Modul site-settings (Singleton-Tabelle SiteSettings) für YouTube-/Instagram-/TikTok-Links, verwaltbar unter Verwaltung > Website (neue Berechtigung site-settings:read/update). Die öffentliche Herkunftsseite (/herkunft/[lot]) zeigt die konfigurierten Links jetzt zusätzlich zur Chargen-Herkunft an - PublicService liefert sie über das bereits oeffentliche /public/batches/:lotNumber mit aus, keine zusaetzliche unauthentifizierte Route noetig. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
1 parent
33fa768062
commit
351f9ef34c
19 files changed
+612
-4
No files matched your search
@@ -1,8 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { SiteSettingsModule } from '../site-settings/site-settings.module';
|
||||
import { PublicController } from './public.controller';
|
||||
import { PublicService } from './public.service';
|
||||
|
||||
@Module({
|
||||
imports: [SiteSettingsModule],
|
||||
controllers: [PublicController],
|
||||
providers: [PublicService],
|
||||
})
|
||||
|
||||
@@ -1,14 +1,26 @@
|
||||
import { NotFoundException } from '@nestjs/common';
|
||||
import { PublicService } from './public.service';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { SiteSettingsService } from '../site-settings/site-settings.service';
|
||||
|
||||
describe('PublicService', () => {
|
||||
let service: PublicService;
|
||||
let prisma: { bottlingBatch: { findUnique: jest.Mock } };
|
||||
let siteSettings: { get: jest.Mock };
|
||||
|
||||
beforeEach(() => {
|
||||
prisma = { bottlingBatch: { findUnique: jest.fn() } };
|
||||
service = new PublicService(prisma as unknown as PrismaService);
|
||||
siteSettings = {
|
||||
get: jest.fn().mockResolvedValue({
|
||||
youtubeUrl: null,
|
||||
instagramUrl: null,
|
||||
tiktokUrl: null,
|
||||
}),
|
||||
};
|
||||
service = new PublicService(
|
||||
prisma as unknown as PrismaService,
|
||||
siteSettings as unknown as SiteSettingsService,
|
||||
);
|
||||
});
|
||||
|
||||
it('throws NotFoundException when the lot does not exist', async () => {
|
||||
@@ -66,4 +78,32 @@ describe('PublicService', () => {
|
||||
expect(result.apiaries).toEqual([{ name: 'Stand Wald', location: 'Wald' }]);
|
||||
expect(result.harvestDates).toEqual([harvestDate1, harvestDate2]);
|
||||
});
|
||||
|
||||
it('includes the configured social links', async () => {
|
||||
siteSettings.get.mockResolvedValue({
|
||||
youtubeUrl: 'https://youtube.com/@wabenhain',
|
||||
instagramUrl: 'https://instagram.com/wabenhain',
|
||||
tiktokUrl: null,
|
||||
});
|
||||
prisma.bottlingBatch.findUnique.mockResolvedValueOnce({
|
||||
lotNumber: 'WH-2026-001',
|
||||
date: new Date('2026-08-20'),
|
||||
bestBeforeDate: new Date('2028-08-20'),
|
||||
jarSize: 500,
|
||||
product: null,
|
||||
honeyBatch: {
|
||||
honeyType: null,
|
||||
waterContent: null,
|
||||
extraction: { date: null, harvests: [] },
|
||||
},
|
||||
});
|
||||
|
||||
const result = await service.findBatchByLot('WH-2026-001');
|
||||
|
||||
expect(result.socialLinks).toEqual({
|
||||
youtube: 'https://youtube.com/@wabenhain',
|
||||
instagram: 'https://instagram.com/wabenhain',
|
||||
tiktok: null,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,11 +1,18 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { SiteSettingsService } from '../site-settings/site-settings.service';
|
||||
|
||||
export interface PublicApiaryOrigin {
|
||||
name: string;
|
||||
location: string | null;
|
||||
}
|
||||
|
||||
export interface PublicSocialLinks {
|
||||
youtube: string | null;
|
||||
instagram: string | null;
|
||||
tiktok: string | null;
|
||||
}
|
||||
|
||||
export interface PublicBatch {
|
||||
lotNumber: string;
|
||||
productName: string | null;
|
||||
@@ -18,11 +25,15 @@ export interface PublicBatch {
|
||||
extractedDate: Date | null;
|
||||
harvestDates: Date[];
|
||||
apiaries: PublicApiaryOrigin[];
|
||||
socialLinks: PublicSocialLinks;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class PublicService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly siteSettings: SiteSettingsService,
|
||||
) {}
|
||||
|
||||
async findBatchByLot(lotNumber: string): Promise<PublicBatch> {
|
||||
const batch = await this.prisma.bottlingBatch.findUnique({
|
||||
@@ -60,6 +71,8 @@ export class PublicService {
|
||||
.sort((a, b) => a - b)
|
||||
.map((time) => new Date(time));
|
||||
|
||||
const settings = await this.siteSettings.get();
|
||||
|
||||
return {
|
||||
lotNumber: batch.lotNumber,
|
||||
productName: batch.product?.name ?? null,
|
||||
@@ -72,6 +85,11 @@ export class PublicService {
|
||||
extractedDate: batch.honeyBatch.extraction.date,
|
||||
harvestDates,
|
||||
apiaries: [...apiaryByName.values()],
|
||||
socialLinks: {
|
||||
youtube: settings.youtubeUrl,
|
||||
instagram: settings.instagramUrl,
|
||||
tiktok: settings.tiktokUrl,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user