Das Abschalten des Aufgaben-Modus wird nun sauber in der App reflektiert- die UI passt sich an und der Admin erhält einen Hinweis, dass die Aufgabenverwaltung nicht verfügbar ist
This commit is contained in:
@@ -21,6 +21,8 @@ import { Sparkles, Award, Trophy, Camera, Users, BarChart2, Flame } from 'lucide
|
||||
import { useTranslation, type TranslateFn } from '../i18n/useTranslation';
|
||||
import type { LocaleCode } from '../i18n/messages';
|
||||
import { localizeTaskLabel } from '../lib/localizeTaskLabel';
|
||||
import { useEventData } from '../hooks/useEventData';
|
||||
import { isTaskModeEnabled } from '../lib/engagement';
|
||||
|
||||
const GENERIC_ERROR = 'GENERIC_ERROR';
|
||||
|
||||
@@ -311,9 +313,10 @@ function Highlights({ topPhoto, trendingEmotion, t, formatRelativeTime, locale,
|
||||
type PersonalActionsProps = {
|
||||
token: string;
|
||||
t: TranslateFn;
|
||||
tasksEnabled: boolean;
|
||||
};
|
||||
|
||||
function PersonalActions({ token, t }: PersonalActionsProps) {
|
||||
function PersonalActions({ token, t, tasksEnabled }: PersonalActionsProps) {
|
||||
return (
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<Button asChild>
|
||||
@@ -322,12 +325,14 @@ function PersonalActions({ token, t }: PersonalActionsProps) {
|
||||
{t('achievements.personal.actions.upload')}
|
||||
</Link>
|
||||
</Button>
|
||||
<Button variant="outline" asChild>
|
||||
<Link to={`/e/${encodeURIComponent(token)}/tasks`} className="flex items-center gap-2">
|
||||
<Sparkles className="h-4 w-4" aria-hidden />
|
||||
{t('achievements.personal.actions.tasks')}
|
||||
</Link>
|
||||
</Button>
|
||||
{tasksEnabled ? (
|
||||
<Button variant="outline" asChild>
|
||||
<Link to={`/e/${encodeURIComponent(token)}/tasks`} className="flex items-center gap-2">
|
||||
<Sparkles className="h-4 w-4" aria-hidden />
|
||||
{t('achievements.personal.actions.tasks')}
|
||||
</Link>
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -336,6 +341,8 @@ export default function AchievementsPage() {
|
||||
const { token } = useParams<{ token: string }>();
|
||||
const identity = useGuestIdentity();
|
||||
const { t, locale } = useTranslation();
|
||||
const { event } = useEventData();
|
||||
const tasksEnabled = isTaskModeEnabled(event);
|
||||
const [data, setData] = useState<AchievementsPayload | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -453,15 +460,15 @@ export default function AchievementsPage() {
|
||||
<CardTitle className="text-lg font-semibold">
|
||||
{t('achievements.personal.greeting', { name: data.personal.guestName || identity.name || t('achievements.leaderboard.guestFallback') })}
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
{t('achievements.personal.stats', {
|
||||
photos: formatNumber(data.personal.photos),
|
||||
tasks: formatNumber(data.personal.tasks),
|
||||
likes: formatNumber(data.personal.likes),
|
||||
})}
|
||||
</CardDescription>
|
||||
<CardDescription>
|
||||
{t('achievements.personal.stats', {
|
||||
photos: formatNumber(data.personal.photos),
|
||||
tasks: formatNumber(data.personal.tasks),
|
||||
likes: formatNumber(data.personal.likes),
|
||||
})}
|
||||
</CardDescription>
|
||||
</div>
|
||||
<PersonalActions token={token} t={t} />
|
||||
<PersonalActions token={token} t={t} tasksEnabled={tasksEnabled} />
|
||||
</CardHeader>
|
||||
</Card>
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import { getEmotionIcon, getEmotionTheme, type EmotionIdentity } from '../lib/em
|
||||
import { getDeviceId } from '../lib/device';
|
||||
import { useDirectUpload } from '../hooks/useDirectUpload';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { isTaskModeEnabled } from '../lib/engagement';
|
||||
|
||||
export default function HomePage() {
|
||||
const { token } = useParams<{ token: string }>();
|
||||
@@ -72,6 +73,7 @@ export default function HomePage() {
|
||||
const secondaryAccent = branding.secondaryColor;
|
||||
const uploadsRequireApproval =
|
||||
(event?.guest_upload_visibility as 'immediate' | 'review' | undefined) !== 'immediate';
|
||||
const tasksEnabled = isTaskModeEnabled(event);
|
||||
|
||||
const [missionDeck, setMissionDeck] = React.useState<MissionPreview[]>([]);
|
||||
const [missionPool, setMissionPool] = React.useState<MissionPreview[]>([]);
|
||||
@@ -232,8 +234,9 @@ export default function HomePage() {
|
||||
}
|
||||
}
|
||||
poolIndexRef.current = restoredIndex;
|
||||
if (!tasksEnabled) return;
|
||||
fetchTasksPage(1, true);
|
||||
}, [fetchTasksPage, locale, sliderStateKey, token]);
|
||||
}, [fetchTasksPage, locale, sliderStateKey, tasksEnabled, token]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (missionPool.length === 0) return;
|
||||
@@ -279,6 +282,34 @@ export default function HomePage() {
|
||||
const introMessage =
|
||||
introArray.length > 0 ? introArray[Math.floor(Math.random() * introArray.length)] : '';
|
||||
|
||||
if (!tasksEnabled) {
|
||||
return (
|
||||
<div className="space-y-3 pb-24" style={bodyFont ? { fontFamily: bodyFont } : undefined}>
|
||||
<section className="space-y-1 px-4">
|
||||
<p className="text-sm font-semibold text-foreground">
|
||||
{t('home.welcomeLine').replace('{name}', displayName)}
|
||||
</p>
|
||||
{introMessage && <p className="text-xs text-muted-foreground">{introMessage}</p>}
|
||||
</section>
|
||||
|
||||
<section className="space-y-2 px-4">
|
||||
<UploadActionCard
|
||||
token={token}
|
||||
accentColor={accentColor}
|
||||
secondaryAccent={secondaryAccent}
|
||||
radius={radius}
|
||||
bodyFont={bodyFont}
|
||||
requiresApproval={uploadsRequireApproval}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Separator />
|
||||
|
||||
<GalleryPreview token={token} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-0.5 pb-24" style={bodyFont ? { fontFamily: bodyFont } : undefined}>
|
||||
<section className="space-y-1 px-4">
|
||||
|
||||
@@ -40,6 +40,7 @@ import { useEventBranding } from '../context/EventBrandingContext';
|
||||
import { compressPhoto, formatBytes } from '../lib/image';
|
||||
import { useGuestIdentity } from '../context/GuestIdentityContext';
|
||||
import { useEventData } from '../hooks/useEventData';
|
||||
import { isTaskModeEnabled } from '../lib/engagement';
|
||||
|
||||
interface Task {
|
||||
id: number;
|
||||
@@ -118,6 +119,8 @@ export default function UploadPage() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const { markCompleted } = useGuestTaskProgress(token);
|
||||
const identity = useGuestIdentity();
|
||||
const { event } = useEventData();
|
||||
const tasksEnabled = isTaskModeEnabled(event);
|
||||
const { t, locale } = useTranslation();
|
||||
const stats = useEventStats();
|
||||
const { branding } = useEventBranding();
|
||||
@@ -233,7 +236,7 @@ const [canUpload, setCanUpload] = useState(true);
|
||||
|
||||
// Load task metadata
|
||||
useEffect(() => {
|
||||
if (!token || taskId === null) {
|
||||
if (!token || taskId === null || !tasksEnabled) {
|
||||
setLoadingTask(false);
|
||||
return;
|
||||
}
|
||||
@@ -249,7 +252,7 @@ const [canUpload, setCanUpload] = useState(true);
|
||||
const fallbackInstructions = t('upload.taskInfo.fallbackInstructions');
|
||||
|
||||
try {
|
||||
setLoadingTask(true);
|
||||
setLoadingTask(true);
|
||||
|
||||
const res = await fetch(
|
||||
`/api/v1/events/${encodeURIComponent(eventKey)}/tasks?locale=${encodeURIComponent(locale)}`,
|
||||
|
||||
Reference in New Issue
Block a user