feat(packages): implement package-based business model
This commit is contained in:
@@ -21,6 +21,7 @@ import {
|
||||
Zap,
|
||||
ZapOff,
|
||||
} from 'lucide-react';
|
||||
import { getEventPackage, type EventPackage } from '../services/eventApi';
|
||||
|
||||
interface Task {
|
||||
id: number;
|
||||
@@ -85,6 +86,9 @@ export default function UploadPage() {
|
||||
const [uploadProgress, setUploadProgress] = useState(0);
|
||||
const [uploadError, setUploadError] = useState<string | null>(null);
|
||||
|
||||
const [eventPackage, setEventPackage] = useState<EventPackage | null>(null);
|
||||
const [canUpload, setCanUpload] = useState(true);
|
||||
|
||||
const [showPrimer, setShowPrimer] = useState<boolean>(() => {
|
||||
if (typeof window === 'undefined') return false;
|
||||
return window.localStorage.getItem(primerStorageKey) !== '1';
|
||||
@@ -201,6 +205,30 @@ export default function UploadPage() {
|
||||
};
|
||||
}, [slug, taskId, emotionSlug]);
|
||||
|
||||
// Check upload limits
|
||||
useEffect(() => {
|
||||
if (!slug || !task) return;
|
||||
|
||||
const checkLimits = async () => {
|
||||
try {
|
||||
const pkg = await getEventPackage(slug);
|
||||
setEventPackage(pkg);
|
||||
if (pkg && pkg.used_photos >= pkg.package.max_photos) {
|
||||
setCanUpload(false);
|
||||
setUploadError('Upload-Limit erreicht. Kontaktieren Sie den Organisator für ein Upgrade.');
|
||||
} else {
|
||||
setCanUpload(true);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to check package limits', err);
|
||||
setCanUpload(false);
|
||||
setUploadError('Fehler beim Prüfen des Limits. Upload deaktiviert.');
|
||||
}
|
||||
};
|
||||
|
||||
checkLimits();
|
||||
}, [slug, task]);
|
||||
|
||||
const stopStream = useCallback(() => {
|
||||
if (streamRef.current) {
|
||||
streamRef.current.getTracks().forEach((track) => track.stop());
|
||||
@@ -428,7 +456,7 @@ export default function UploadPage() {
|
||||
);
|
||||
|
||||
const handleUsePhoto = useCallback(async () => {
|
||||
if (!slug || !reviewPhoto || !task) return;
|
||||
if (!slug || !reviewPhoto || !task || !canUpload) return;
|
||||
setMode('uploading');
|
||||
setUploadProgress(5);
|
||||
setUploadError(null);
|
||||
@@ -459,9 +487,10 @@ export default function UploadPage() {
|
||||
}
|
||||
setStatusMessage('');
|
||||
}
|
||||
}, [emotionSlug, markCompleted, navigateAfterUpload, reviewPhoto, slug, stopStream, task]);
|
||||
}, [emotionSlug, markCompleted, navigateAfterUpload, reviewPhoto, slug, stopStream, task, canUpload]);
|
||||
|
||||
const handleGalleryPick = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (!canUpload) return;
|
||||
const file = event.target.files?.[0];
|
||||
if (!file) return;
|
||||
setUploadError(null);
|
||||
@@ -474,7 +503,7 @@ export default function UploadPage() {
|
||||
setUploadError('Auswahl fehlgeschlagen. Bitte versuche es erneut.');
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}, []);
|
||||
}, [canUpload]);
|
||||
|
||||
const difficultyBadgeClass = useMemo(() => {
|
||||
if (!task) return 'text-white';
|
||||
@@ -491,6 +520,8 @@ export default function UploadPage() {
|
||||
const isCameraActive = permissionState === 'granted' && mode !== 'uploading';
|
||||
const showTaskOverlay = task && mode !== 'uploading';
|
||||
|
||||
const isUploadDisabled = !canUpload || !task;
|
||||
|
||||
useEffect(() => () => {
|
||||
resetCountdownTimer();
|
||||
if (uploadProgressTimerRef.current) {
|
||||
@@ -527,6 +558,24 @@ export default function UploadPage() {
|
||||
);
|
||||
}
|
||||
|
||||
if (!canUpload) {
|
||||
return (
|
||||
<div className="pb-16">
|
||||
<Header slug={slug} title="Kamera" />
|
||||
<main className="px-4 py-6">
|
||||
<Alert variant="destructive">
|
||||
<AlertTriangle className="h-4 w-4" />
|
||||
<AlertDescription>
|
||||
Upload-Limit erreicht ({eventPackage?.used_photos || 0} / {eventPackage?.package.max_photos || 0} Fotos).
|
||||
Kontaktieren Sie den Organisator für ein Package-Upgrade.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
</main>
|
||||
<BottomNav />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const renderPrimer = () => (
|
||||
showPrimer && (
|
||||
<div className="mx-4 mt-3 rounded-xl border border-pink-200 bg-white/90 p-4 text-sm text-pink-900 shadow">
|
||||
|
||||
Reference in New Issue
Block a user