chore(slideshow): renumber migrations to 138/139 (after whatsapp #649's 137)
PR #649 takes migration 137 (add_whatsapp_template_language). Renumber the slideshow migrations to slot in after it: - 137_add_slideshow_share.js -> 138_add_slideshow_share.js - 138_add_slideshow_styling.js -> 139_add_slideshow_styling.js and update the slideshow migration-number references in comments/types. No content change — both are additive + addColumnIfNotExists-guarded, so re-running under the new filename on an already-migrated DB is a safe no-op.
This commit is contained in:
+2
-2
@@ -1,6 +1,6 @@
|
||||
const { addColumnIfNotExists } = require('../helpers');
|
||||
|
||||
// Live Slideshow styling (migration 137 follow-up):
|
||||
// Live Slideshow styling (migration 138 follow-up):
|
||||
// - a ZDF/ARD-ident-style watermark: a white, semi-transparent logo in a
|
||||
// corner of the projected slideshow (sourced from the site branding logo
|
||||
// or the event's own logo).
|
||||
@@ -54,5 +54,5 @@ exports.up = async function up(knex) {
|
||||
};
|
||||
|
||||
exports.down = async function down() {
|
||||
// Safe rollback - intentionally no-op to avoid data loss (mirrors 074/137).
|
||||
// Safe rollback - intentionally no-op to avoid data loss (mirrors 074/138).
|
||||
};
|
||||
@@ -1882,7 +1882,7 @@ router.post('/:id/toggle-status', adminAuth, requirePermission('events.edit'), r
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Live Slideshow ("Diashow") — a token-only fullscreen kiosk link for live
|
||||
// events that auto-picks-up new uploads (migration 137). Mirrors the
|
||||
// events that auto-picks-up new uploads (migration 138). Mirrors the
|
||||
// client-access second-token pattern: the link is minted on demand, rotatable
|
||||
// and disable-able, independent of the gallery password / share link.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -290,7 +290,7 @@ router.put('/accounting', adminAuth, requirePermission('settings.edit'), async (
|
||||
}
|
||||
});
|
||||
|
||||
// Global Live Slideshow defaults (migration 138). The per-event watermark is
|
||||
// Global Live Slideshow defaults (migration 139). The per-event watermark is
|
||||
// tri-state (events.show_watermark NULL = inherit these). Read via the generic
|
||||
// GET /:type ('slideshow'); this is the typed write.
|
||||
router.put('/slideshow', adminAuth, requirePermission('settings.edit'), async (req, res) => {
|
||||
|
||||
@@ -220,7 +220,7 @@ router.get('/:slug/info', async (req, res) => {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Live Slideshow ("Diashow") — token-only fullscreen kiosk surface
|
||||
// (migration 137). The token in the URL IS the secret (no gallery password),
|
||||
// (migration 138). The token in the URL IS the secret (no gallery password),
|
||||
// so these routes are unauthenticated except for the token match itself. The
|
||||
// slideshow shows ALL public/visible, finished photos — exactly the guest
|
||||
// set — so once /session mints a short-lived `accessLevel:'slideshow'` JWT,
|
||||
@@ -586,7 +586,7 @@ router.get('/:slug/photos', verifyGalleryAccess, resolveGuest, async (req, res)
|
||||
// Log view — but NOT for the Live Slideshow kiosk. A running projector
|
||||
// refetches this list on every new-upload poll, which would massively
|
||||
// inflate total_views / unique_visitors. The slideshow is explicitly
|
||||
// excluded from real visitor analytics (migration 137 design).
|
||||
// excluded from real visitor analytics (migration 138 design).
|
||||
if (req.accessLevel !== 'slideshow') {
|
||||
await db('access_logs').insert({
|
||||
event_id: req.event.id,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* <SlideshowSettingsCard>
|
||||
*
|
||||
* Per-event "Live Slideshow" ("Diashow") admin surface (migrations 137/138).
|
||||
* Per-event "Live Slideshow" ("Diashow") admin surface (migrations 138/139).
|
||||
* A token-only fullscreen kiosk link for live events that auto-picks-up new
|
||||
* uploads while it runs. Mounted once on the EventDetailsPage; admin can:
|
||||
* - Generate the slideshow link on demand (mints show_share_token)
|
||||
|
||||
@@ -2159,7 +2159,7 @@ export const EventDetailsPage: React.FC = () => {
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Live Slideshow ("Diashow") link + live display settings (migrations 137/138).
|
||||
{/* Live Slideshow ("Diashow") link + live display settings (migrations 138/139).
|
||||
Gated behind the `slideshow` feature flag. */}
|
||||
{flags.slideshow && (
|
||||
<SlideshowSettingsCard
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import { vi } from 'vitest';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
|
||||
import { GALLERY_THEME_PRESETS } from '../../../types/theme.types';
|
||||
|
||||
const CSS = '.gallery-premium-footer p:first-child { display: none; }';
|
||||
const savedTheme = { ...GALLERY_THEME_PRESETS.galleryPremium.config, customCss: CSS };
|
||||
|
||||
// --- mocks -----------------------------------------------------------------
|
||||
vi.mock('react-i18next', async () => {
|
||||
const actual = await vi.importActual<typeof import('react-i18next')>('react-i18next');
|
||||
return {
|
||||
...actual,
|
||||
useTranslation: () => ({ t: (k: string, fb?: unknown) => (typeof fb === 'string' ? fb : k) })
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('react-toastify', () => ({ toast: { success: vi.fn(), error: vi.fn() } }));
|
||||
|
||||
vi.mock('../../../services/settings.service', async () => {
|
||||
const actual = await vi.importActual<any>('../../../services/settings.service');
|
||||
return {
|
||||
...actual,
|
||||
settingsService: {
|
||||
...actual.settingsService,
|
||||
getSettingsByType: vi.fn(async (type: string) =>
|
||||
type === 'theme' ? { theme_config: savedTheme } : {}
|
||||
),
|
||||
getAllSettings: vi.fn(async () => ({ thumbnail_width: '800', thumbnail_height: '800' })),
|
||||
updateTheme: vi.fn(async () => undefined),
|
||||
updateBranding: vi.fn(async () => undefined),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('../../../services/fonts.service', () => ({
|
||||
fontsService: { list: vi.fn(async () => []) },
|
||||
extractFamilyName: (s: string) => s,
|
||||
}));
|
||||
|
||||
vi.mock('../../../services/businessProfile.service', () => ({
|
||||
businessProfileService: { get: vi.fn(async () => ({ profile: {} })), update: vi.fn() },
|
||||
}));
|
||||
|
||||
vi.mock('../../../hooks/usePublicSettings', () => ({
|
||||
PUBLIC_SETTINGS_QUERY_KEY: ['public-settings'],
|
||||
usePublicSettings: () => ({ data: { branding_force_color_mode: null } }),
|
||||
}));
|
||||
|
||||
vi.mock('../../../contexts/FeatureFlagsContext', () => ({
|
||||
useFeatureFlags: () => ({ flags: {} }),
|
||||
useFeatureEnabled: () => false,
|
||||
}));
|
||||
|
||||
// Heavy/unrelated children — stub to keep the test focused on the CSS textarea.
|
||||
vi.mock('../../../components/admin/CustomerDashboardBrandingCard', () => ({
|
||||
CustomerDashboardBrandingCard: () => null,
|
||||
}));
|
||||
vi.mock('../../../components/admin/PdfTypographyCard', () => ({
|
||||
PdfTypographyCard: () => null,
|
||||
}));
|
||||
vi.mock('../../../components/admin', async () => {
|
||||
const actual = await vi.importActual<any>('../../../components/admin');
|
||||
return { ...actual, GalleryPreview: () => null };
|
||||
});
|
||||
|
||||
import { BrandingPage } from '../BrandingPage';
|
||||
import { ThemeProvider } from '../../../contexts/ThemeContext';
|
||||
|
||||
describe('BrandingPage custom CSS persistence (#645)', () => {
|
||||
beforeEach(() => localStorage.clear());
|
||||
|
||||
it('shows the saved custom CSS in the textarea on load for a Gallery Premium theme', async () => {
|
||||
const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
render(
|
||||
<QueryClientProvider client={qc}>
|
||||
<ThemeProvider>
|
||||
<BrandingPage />
|
||||
</ThemeProvider>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
|
||||
const textarea = (await screen.findByPlaceholderText(
|
||||
'/* Add custom CSS here */'
|
||||
)) as HTMLTextAreaElement;
|
||||
|
||||
await waitFor(() => expect(textarea.value).toBe(CSS));
|
||||
});
|
||||
});
|
||||
@@ -67,7 +67,7 @@ export interface Event {
|
||||
// Client access (#172)
|
||||
client_access_enabled?: boolean;
|
||||
client_share_token?: string;
|
||||
// Live Slideshow / "Diashow" (migration 137). Token-only fullscreen kiosk
|
||||
// Live Slideshow / "Diashow" (migration 138). Token-only fullscreen kiosk
|
||||
// link minted on demand; null token = disabled. Settings drive the running
|
||||
// projector and can be changed live.
|
||||
show_share_token?: string | null;
|
||||
|
||||
Reference in New Issue
Block a user