Update release notes and deploy workspace
This commit is contained in:
@@ -24,9 +24,22 @@ const adminWorkspacePermissions = new Set([
|
||||
'risk',
|
||||
'audit',
|
||||
'analytics',
|
||||
'voting',
|
||||
'winners',
|
||||
])
|
||||
|
||||
function hasEffectivePermission(permissionKeys: string[] = [], permissionKey: string) {
|
||||
if (permissionKeys.includes(permissionKey)) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (permissionKey === 'voting') {
|
||||
return permissionKeys.includes('analytics') || permissionKeys.includes('winners')
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function readStoredToken() {
|
||||
if (typeof window === 'undefined') return null
|
||||
return window.localStorage.getItem(AUTH_TOKEN_KEY)
|
||||
@@ -56,7 +69,7 @@ export const useAuthStore = defineStore('auth', {
|
||||
canManageAdminWorkspace: (state) => adminWorkspaceRoles.has(state.session?.role ?? '') || (state.session?.permissionKeys ?? []).some((key) => adminWorkspacePermissions.has(key)),
|
||||
canManageOperationalSettings: (state) => state.session?.role === 'owner' || state.session?.role === 'creator',
|
||||
canManageTeam: (state) => (state.session?.permissionKeys ?? []).includes('team'),
|
||||
hasPermission: (state) => (permissionKey: string) => (state.session?.permissionKeys ?? []).includes(permissionKey),
|
||||
hasPermission: (state) => (permissionKey: string) => hasEffectivePermission(state.session?.permissionKeys ?? [], permissionKey),
|
||||
isOwner: (state) => state.session?.role === 'owner',
|
||||
isCreator: (state) => state.session?.role === 'creator',
|
||||
isOwnerOrCreator: (state) => state.session?.role === 'owner' || state.session?.role === 'creator',
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
classifyPublicLoadError,
|
||||
createAwardsState,
|
||||
createEmptyAdminDashboard,
|
||||
createEmptyAdminOperationalSettings,
|
||||
createEmptyAdminRiskFlagsResponse,
|
||||
createEmptyAdminSeasonDetail,
|
||||
createEmptyAdminOptionalFeatureSettings,
|
||||
@@ -65,12 +66,10 @@ export const useAwardsStore = defineStore('awards', {
|
||||
this.categories = await api.getSeasonCategories(this.overview.year)
|
||||
await this.loadPublicSponsors(this.overview.year)
|
||||
const overviewArchiveYears = this.overview.archiveYears ?? []
|
||||
const initialArchiveYear = canExposeCurrentSeasonWinners(this.overview.currentPhase)
|
||||
? this.overview.year
|
||||
: overviewArchiveYears[0]?.year
|
||||
?? this.overview.winnersPreview[0]?.year
|
||||
?? this.overview.year - 1
|
||||
this.archive = await api.getWinnerArchive(initialArchiveYear)
|
||||
const initialArchiveYear = overviewArchiveYears[0]?.year ?? this.overview.year - 1
|
||||
this.archive = overviewArchiveYears.length > 0
|
||||
? await api.getWinnerArchive(initialArchiveYear)
|
||||
: createEmptyArchive(initialArchiveYear)
|
||||
this.apiMode = 'api'
|
||||
} catch (error) {
|
||||
this.apiMode = 'fallback'
|
||||
@@ -102,18 +101,18 @@ export const useAwardsStore = defineStore('awards', {
|
||||
},
|
||||
async loadAdmin() {
|
||||
try {
|
||||
const [admin, adminSeasons, adminSiteSettings, adminOptionalFeatureSettings, databaseHealth] = await Promise.all([
|
||||
api.getAdminDashboard(),
|
||||
const [adminSeasons, adminSiteSettings, adminOptionalFeatureSettings, adminOperationalSettings, databaseHealth] = await Promise.all([
|
||||
api.getAdminSeasons(),
|
||||
api.getAdminSiteSettings(),
|
||||
api.getAdminOptionalFeatureSettings(),
|
||||
api.getDatabaseHealth(),
|
||||
api.getAdminSiteSettings().catch(() => createEmptyAdminSiteSettings()),
|
||||
api.getAdminOptionalFeatureSettings().catch(() => createEmptyAdminOptionalFeatureSettings()),
|
||||
api.getAdminOperationalSettings().catch(() => createEmptyAdminOperationalSettings()),
|
||||
api.getDatabaseHealth().catch(() => createEmptyDatabaseHealth()),
|
||||
])
|
||||
|
||||
this.admin = admin
|
||||
this.adminSeasons = adminSeasons
|
||||
this.adminSiteSettings = adminSiteSettings
|
||||
this.adminOptionalFeatureSettings = adminOptionalFeatureSettings
|
||||
this.adminOperationalSettings = adminOperationalSettings
|
||||
this.databaseHealth = databaseHealth
|
||||
|
||||
if (!this.adminSelectedSeasonId || !this.adminSeasons.some((season) => season.id === this.adminSelectedSeasonId)) {
|
||||
@@ -121,17 +120,20 @@ export const useAwardsStore = defineStore('awards', {
|
||||
}
|
||||
|
||||
if (this.adminSelectedSeasonId) {
|
||||
const [seasonDetail, sponsors, showacts, adminWorkflowRules] = await Promise.all([
|
||||
const [admin, seasonDetail, sponsors, showacts, adminWorkflowRules] = await Promise.all([
|
||||
api.getAdminDashboard(this.adminSelectedSeasonId),
|
||||
api.getAdminSeasonDetail(this.adminSelectedSeasonId),
|
||||
api.getAdminSponsors(this.adminSelectedSeasonId),
|
||||
api.getAdminShowactApplications(this.adminSelectedSeasonId),
|
||||
api.getAdminWorkflowRules(this.adminSelectedSeasonId),
|
||||
api.getAdminSponsors(this.adminSelectedSeasonId).catch(() => []),
|
||||
api.getAdminShowactApplications(this.adminSelectedSeasonId).catch(() => []),
|
||||
api.getAdminWorkflowRules(this.adminSelectedSeasonId).catch(() => createEmptyAdminWorkflowRulesResponse()),
|
||||
])
|
||||
this.admin = admin
|
||||
this.adminSeasonDetail = normalizeSeasonDetail(seasonDetail)
|
||||
this.adminSponsors = sponsors
|
||||
this.adminShowactApplications = showacts
|
||||
this.adminWorkflowRules = adminWorkflowRules
|
||||
} else {
|
||||
this.admin = createEmptyAdminDashboard()
|
||||
this.adminSeasonDetail = createEmptyAdminSeasonDetail()
|
||||
this.adminSponsors = []
|
||||
this.adminShowactApplications = []
|
||||
@@ -144,6 +146,7 @@ export const useAwardsStore = defineStore('awards', {
|
||||
this.adminSeasons = []
|
||||
this.adminSeasonDetail = createEmptyAdminSeasonDetail()
|
||||
this.adminSiteSettings = createEmptyAdminSiteSettings()
|
||||
this.adminOperationalSettings = createEmptyAdminOperationalSettings()
|
||||
this.adminOptionalFeatureSettings = createEmptyAdminOptionalFeatureSettings()
|
||||
this.adminWorkflowRules = createEmptyAdminWorkflowRulesResponse()
|
||||
this.adminTrackingRules = createEmptyAdminTrackingRulesResponse()
|
||||
@@ -158,19 +161,22 @@ export const useAwardsStore = defineStore('awards', {
|
||||
},
|
||||
async loadAdminContentWorkspace() {
|
||||
try {
|
||||
const [adminSiteSettings, adminOptionalFeatureSettings, databaseHealth] = await Promise.all([
|
||||
api.getAdminSiteSettings(),
|
||||
api.getAdminOptionalFeatureSettings(),
|
||||
api.getDatabaseHealth(),
|
||||
const [adminSiteSettings, adminOptionalFeatureSettings, adminOperationalSettings, databaseHealth] = await Promise.all([
|
||||
api.getAdminSiteSettings().catch(() => createEmptyAdminSiteSettings()),
|
||||
api.getAdminOptionalFeatureSettings().catch(() => createEmptyAdminOptionalFeatureSettings()),
|
||||
api.getAdminOperationalSettings().catch(() => createEmptyAdminOperationalSettings()),
|
||||
api.getDatabaseHealth().catch(() => createEmptyDatabaseHealth()),
|
||||
])
|
||||
|
||||
this.adminSiteSettings = adminSiteSettings
|
||||
this.adminOptionalFeatureSettings = adminOptionalFeatureSettings
|
||||
this.adminOperationalSettings = adminOperationalSettings
|
||||
this.databaseHealth = databaseHealth
|
||||
this.apiMode = 'api'
|
||||
} catch {
|
||||
this.apiMode = 'fallback'
|
||||
this.adminSiteSettings = createEmptyAdminSiteSettings()
|
||||
this.adminOperationalSettings = createEmptyAdminOperationalSettings()
|
||||
this.adminOptionalFeatureSettings = createEmptyAdminOptionalFeatureSettings()
|
||||
this.databaseHealth = createEmptyDatabaseHealth()
|
||||
}
|
||||
@@ -189,12 +195,14 @@ export const useAwardsStore = defineStore('awards', {
|
||||
async loadAdminSeasonDetail(seasonId: number) {
|
||||
try {
|
||||
this.adminSelectedSeasonId = seasonId
|
||||
const [seasonDetail, sponsors, showacts, adminWorkflowRules] = await Promise.all([
|
||||
const [admin, seasonDetail, sponsors, showacts, adminWorkflowRules] = await Promise.all([
|
||||
api.getAdminDashboard(seasonId),
|
||||
api.getAdminSeasonDetail(seasonId),
|
||||
api.getAdminSponsors(seasonId),
|
||||
api.getAdminShowactApplications(seasonId),
|
||||
api.getAdminWorkflowRules(seasonId),
|
||||
api.getAdminSponsors(seasonId).catch(() => []),
|
||||
api.getAdminShowactApplications(seasonId).catch(() => []),
|
||||
api.getAdminWorkflowRules(seasonId).catch(() => createEmptyAdminWorkflowRulesResponse()),
|
||||
])
|
||||
this.admin = admin
|
||||
this.adminSeasonDetail = normalizeSeasonDetail(seasonDetail)
|
||||
this.adminSponsors = sponsors
|
||||
this.adminShowactApplications = showacts
|
||||
@@ -202,6 +210,7 @@ export const useAwardsStore = defineStore('awards', {
|
||||
this.apiMode = 'api'
|
||||
} catch {
|
||||
this.apiMode = 'fallback'
|
||||
this.admin = createEmptyAdminDashboard()
|
||||
this.adminSeasonDetail = createEmptyAdminSeasonDetail()
|
||||
this.adminSponsors = []
|
||||
this.adminShowactApplications = []
|
||||
@@ -416,6 +425,16 @@ export const useAwardsStore = defineStore('awards', {
|
||||
await this.refreshAdminSeasonWorkspace(seasonId, { reloadAdmin: true, reloadHome: true })
|
||||
return result
|
||||
},
|
||||
async publishAdminWinners(seasonId: number) {
|
||||
const result = await api.publishAdminWinners(seasonId)
|
||||
await this.refreshAdminSeasonWorkspace(seasonId, { reloadAdmin: true, reloadHome: true })
|
||||
return result
|
||||
},
|
||||
async unpublishAdminWinners(seasonId: number) {
|
||||
const result = await api.unpublishAdminWinners(seasonId)
|
||||
await this.refreshAdminSeasonWorkspace(seasonId, { reloadAdmin: true, reloadHome: true })
|
||||
return result
|
||||
},
|
||||
async approveAdminNomination(nominationId: number, seasonId: number, payload: ApproveNominationPayload) {
|
||||
const result = await api.approveAdminNomination(nominationId, payload)
|
||||
await this.refreshAdminSeasonWorkspace(seasonId, { reloadAdmin: true, reloadHome: true })
|
||||
@@ -545,12 +564,4 @@ export const useAwardsStore = defineStore('awards', {
|
||||
},
|
||||
})
|
||||
|
||||
function canExposeCurrentSeasonWinners(currentPhase: string) {
|
||||
const normalized = currentPhase.trim().toLowerCase()
|
||||
return normalized.includes('abgeschlossen')
|
||||
|| normalized.includes('archiv')
|
||||
|| normalized.includes('complete')
|
||||
|| normalized.includes('ended')
|
||||
}
|
||||
|
||||
export type AwardsStore = ReturnType<typeof useAwardsStore>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { ApiRequestError } from '../../lib/http'
|
||||
import type {
|
||||
AdminDashboardResponse,
|
||||
AdminOptionalFeatureSettingsResponse,
|
||||
AdminOperationalSettingsResponse,
|
||||
AdminRiskFlag,
|
||||
AdminRiskFlagsResponse,
|
||||
AdminSeasonDetailResponse,
|
||||
@@ -28,7 +29,6 @@ export function createEmptyOverview(): OverviewResponse {
|
||||
title: '',
|
||||
showDate: '',
|
||||
showStartsAt: '20:00:00',
|
||||
showStreamUrl: '',
|
||||
currentPhase: '',
|
||||
isCommunityOnly: true,
|
||||
loginProvider: 'Twitch',
|
||||
@@ -44,6 +44,24 @@ export function createEmptyOverview(): OverviewResponse {
|
||||
shareDiscordUrl: '',
|
||||
privacyEmail: '',
|
||||
privacyPolicyContent: '',
|
||||
awardsSectionTitle: '',
|
||||
awardsSectionDescription: '',
|
||||
subcategoriesSectionTitle: '',
|
||||
subcategoriesSectionDescription: '',
|
||||
streamBanner: {
|
||||
eyebrow: 'Das grosse Finale',
|
||||
title: 'Award-Show Finale',
|
||||
text: 'Finale, Countdown und Stream-Link an einem Ort. Sei live dabei, wenn die Community ihre Stars feiert.',
|
||||
liveButtonLabel: 'Jetzt live · Zum Stream',
|
||||
liveButtonUrl: '',
|
||||
lockedButtonLabel: 'Stream noch gesperrt',
|
||||
useCompletedContent: false,
|
||||
completedEyebrow: 'Danke fürs Mitfiebern',
|
||||
completedTitle: 'Award-Show abgeschlossen',
|
||||
completedText: 'Die grosse Award-Show ist vorbei. Danke an alle, die nominiert, gevotet und live mitgefiebert haben.',
|
||||
completedButtonLabel: 'Highlights ansehen',
|
||||
completedButtonUrl: '',
|
||||
},
|
||||
socialLinks: [],
|
||||
footerLinks: [],
|
||||
},
|
||||
@@ -87,6 +105,10 @@ export function createEmptyArchive(year = new Date().getFullYear() - 1): WinnerA
|
||||
|
||||
export function createEmptyAdminDashboard(): AdminDashboardResponse {
|
||||
return {
|
||||
seasonId: 0,
|
||||
year: 0,
|
||||
seasonName: '',
|
||||
isCurrent: false,
|
||||
metrics: [],
|
||||
activities: [],
|
||||
topCategories: [],
|
||||
@@ -95,6 +117,27 @@ export function createEmptyAdminDashboard(): AdminDashboardResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export function createEmptyAdminOperationalSettings(): AdminOperationalSettingsResponse {
|
||||
return {
|
||||
demoLoginManagedByDatabase: false,
|
||||
demoLoginEnabled: false,
|
||||
demoLoginEmail: '',
|
||||
demoLoginPasswordSet: false,
|
||||
demoLoginTwitchUserId: '',
|
||||
demoLoginDisplayName: '',
|
||||
twitchAuthManagedByDatabase: false,
|
||||
twitchAuthConfigured: false,
|
||||
twitchClientId: '',
|
||||
twitchClientSecretSet: false,
|
||||
twitchRedirectUri: '',
|
||||
twitchScope: '',
|
||||
sessionIdleTimeoutHours: 12,
|
||||
maintenanceModeEnabled: false,
|
||||
maintenanceTitle: 'Sternenpause',
|
||||
maintenanceMessage: 'Die Award-Galaxie wird gerade liebevoll poliert. Schau gleich wieder vorbei.',
|
||||
}
|
||||
}
|
||||
|
||||
export function createEmptyAdminRiskFlagsResponse(): AdminRiskFlagsResponse {
|
||||
return {
|
||||
items: [],
|
||||
@@ -135,7 +178,7 @@ export function createEmptyAdminSeasonDetail(): AdminSeasonDetailResponse {
|
||||
id: 0,
|
||||
year: 0,
|
||||
name: '',
|
||||
showStreamUrl: '',
|
||||
isDemo: false,
|
||||
currentPhase: '',
|
||||
isCurrent: false,
|
||||
isCommunityOnly: true,
|
||||
@@ -147,6 +190,8 @@ export function createEmptyAdminSeasonDetail(): AdminSeasonDetailResponse {
|
||||
reviewEndsAt: '',
|
||||
showDate: '',
|
||||
showStartsAt: '20:00:00',
|
||||
winnersPublishedAt: null,
|
||||
winnersPublishedByTwitchId: null,
|
||||
subcategoryTemplates: [],
|
||||
categories: [],
|
||||
candidates: [],
|
||||
@@ -156,6 +201,17 @@ export function createEmptyAdminSeasonDetail(): AdminSeasonDetailResponse {
|
||||
trackingReviewNotes: '',
|
||||
showTrackingReviewNotes: true,
|
||||
results: [],
|
||||
votingWorkspace: {
|
||||
summary: {
|
||||
totalVotes: 0,
|
||||
totalSubcategories: 0,
|
||||
votedSubcategories: 0,
|
||||
readySubcategories: 0,
|
||||
problemSubcategories: 0,
|
||||
winnerSetSubcategories: 0,
|
||||
},
|
||||
categories: [],
|
||||
},
|
||||
clipSubmissions: [],
|
||||
}
|
||||
}
|
||||
@@ -179,6 +235,22 @@ export function createEmptyAdminSiteSettings(): AdminSiteSettingsResponse {
|
||||
sponsorsContent: '',
|
||||
showactsUrl: '',
|
||||
showactsContent: '',
|
||||
streamBannerEyebrow: 'Das grosse Finale',
|
||||
streamBannerTitle: 'Award-Show Finale',
|
||||
streamBannerText: 'Finale, Countdown und Stream-Link an einem Ort. Sei live dabei, wenn die Community ihre Stars feiert.',
|
||||
streamBannerLiveButtonLabel: 'Jetzt live · Zum Stream',
|
||||
streamBannerLiveButtonUrl: '',
|
||||
streamBannerLockedButtonLabel: 'Stream noch gesperrt',
|
||||
streamBannerUseCompletedContent: false,
|
||||
streamBannerCompletedEyebrow: 'Danke fürs Mitfiebern',
|
||||
streamBannerCompletedTitle: 'Award-Show abgeschlossen',
|
||||
streamBannerCompletedText: 'Die grosse Award-Show ist vorbei. Danke an alle, die nominiert, gevotet und live mitgefiebert haben.',
|
||||
streamBannerCompletedButtonLabel: 'Highlights ansehen',
|
||||
streamBannerCompletedButtonUrl: '',
|
||||
awardsSectionTitle: '',
|
||||
awardsSectionDescription: '',
|
||||
subcategoriesSectionTitle: '',
|
||||
subcategoriesSectionDescription: '',
|
||||
socialLinks: [],
|
||||
faq: [],
|
||||
showactFormSchemaJson: '[]',
|
||||
@@ -221,6 +293,7 @@ export function createAwardsState() {
|
||||
adminSeasons: [] as AdminSeasonListItem[],
|
||||
adminSeasonDetail: createEmptyAdminSeasonDetail(),
|
||||
adminSiteSettings: createEmptyAdminSiteSettings(),
|
||||
adminOperationalSettings: createEmptyAdminOperationalSettings(),
|
||||
adminOptionalFeatureSettings: createEmptyAdminOptionalFeatureSettings(),
|
||||
adminWorkflowRules: createEmptyAdminWorkflowRulesResponse(),
|
||||
adminTrackingRules: createEmptyAdminTrackingRulesResponse(),
|
||||
@@ -245,6 +318,8 @@ export function createAwardsState() {
|
||||
export function normalizeSeasonDetail(detail: AdminSeasonDetailResponse): AdminSeasonDetailResponse {
|
||||
return {
|
||||
...detail,
|
||||
winnersPublishedAt: detail.winnersPublishedAt ?? null,
|
||||
winnersPublishedByTwitchId: detail.winnersPublishedByTwitchId ?? null,
|
||||
subcategoryTemplates: detail.subcategoryTemplates ?? [],
|
||||
categories: detail.categories ?? [],
|
||||
candidates: (detail.candidates ?? []).map((candidate) => ({
|
||||
@@ -263,6 +338,20 @@ export function normalizeSeasonDetail(detail: AdminSeasonDetailResponse): AdminS
|
||||
reviewedNominations: detail.reviewedNominations ?? [],
|
||||
trackingReviewNotes: detail.trackingReviewNotes ?? '',
|
||||
results: detail.results ?? [],
|
||||
votingWorkspace: {
|
||||
summary: {
|
||||
totalVotes: detail.votingWorkspace?.summary?.totalVotes ?? 0,
|
||||
totalSubcategories: detail.votingWorkspace?.summary?.totalSubcategories ?? 0,
|
||||
votedSubcategories: detail.votingWorkspace?.summary?.votedSubcategories ?? 0,
|
||||
readySubcategories: detail.votingWorkspace?.summary?.readySubcategories ?? 0,
|
||||
problemSubcategories: detail.votingWorkspace?.summary?.problemSubcategories ?? 0,
|
||||
winnerSetSubcategories: detail.votingWorkspace?.summary?.winnerSetSubcategories ?? 0,
|
||||
},
|
||||
categories: (detail.votingWorkspace?.categories ?? []).map((category) => ({
|
||||
...category,
|
||||
leaderboard: category.leaderboard ?? [],
|
||||
})),
|
||||
},
|
||||
clipSubmissions: detail.clipSubmissions ?? [],
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user