mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-11-02 02:19:43 +00:00
Add question types, nudge feature, and share modal
- Add new question card components (photo, audio, text, who-likely) - Add share preview modal for matches - Add nudge notification system with rate limiting - Add session-based notification improvements - Add match confetti celebration animation - Update onboarding flow with paywall tracking - Remove deprecated swipe card and tutorial components - Update GlassCard/GlassInput to use solid backgrounds - Fix routing issues after packs screen removal
This commit is contained in:
1 parent
8612b16b93
commit
1daf386765
114 files changed
+14877
-7911
No files matched your search
@@ -37,7 +37,6 @@ import { Plus, Pencil, Trash2, Zap, Loader2, Clock } from 'lucide-react';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { toast } from 'sonner';
|
||||
import { IconPreview } from '@/components/ui/icon-picker';
|
||||
import { INTENSITY_LEVELS } from '@/lib/openai';
|
||||
|
||||
// =============================================================================
|
||||
// Types
|
||||
@@ -71,8 +70,7 @@ const DURATION_OPTIONS = [
|
||||
{ value: '168', label: '1 week' },
|
||||
];
|
||||
|
||||
// Helper to get intensity info by level (1-indexed)
|
||||
const getIntensity = (level: number) => INTENSITY_LEVELS[level - 1] || INTENSITY_LEVELS[0];
|
||||
const getDefaultIntensity = (isExplicit?: boolean) => (isExplicit ? 4 : 2);
|
||||
|
||||
// Format duration for display
|
||||
const formatDuration = (hours: number | null): string => {
|
||||
@@ -107,7 +105,7 @@ export function DaresPage() {
|
||||
suggested_duration_hours: string;
|
||||
}>({
|
||||
text: '',
|
||||
intensity: 1,
|
||||
intensity: getDefaultIntensity(),
|
||||
suggested_duration_hours: '',
|
||||
});
|
||||
|
||||
@@ -169,7 +167,7 @@ export function DaresPage() {
|
||||
|
||||
const openCreateDialog = () => {
|
||||
setEditingDare(null);
|
||||
setFormData({ text: '', intensity: 1, suggested_duration_hours: '' });
|
||||
setFormData({ text: '', intensity: getDefaultIntensity(pack?.is_explicit), suggested_duration_hours: '' });
|
||||
setDialogOpen(true);
|
||||
};
|
||||
|
||||
@@ -343,25 +341,6 @@ export function DaresPage() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>Intensity Level</Label>
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{INTENSITY_LEVELS.map((intensity) => (
|
||||
<Button
|
||||
key={intensity.level}
|
||||
type="button"
|
||||
variant={formData.intensity === intensity.level ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
onClick={() => setFormData(d => ({ ...d, intensity: intensity.level }))}
|
||||
className={formData.intensity === intensity.level ? intensity.color : ''}
|
||||
title={intensity.description}
|
||||
>
|
||||
{intensity.level} - {intensity.label}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>Suggested Duration</Label>
|
||||
<Select
|
||||
@@ -467,7 +446,6 @@ export function DaresPage() {
|
||||
</TableHead>
|
||||
<TableHead className="w-12">#</TableHead>
|
||||
<TableHead>Dare</TableHead>
|
||||
<TableHead className="w-24">Intensity</TableHead>
|
||||
<TableHead className="w-24">Duration</TableHead>
|
||||
<TableHead className="w-24">Actions</TableHead>
|
||||
</TableRow>
|
||||
@@ -491,14 +469,6 @@ export function DaresPage() {
|
||||
<TableCell>
|
||||
<span className="line-clamp-2">{dare.text}</span>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge
|
||||
className={`${getIntensity(dare.intensity).color} text-white`}
|
||||
title={getIntensity(dare.intensity).description}
|
||||
>
|
||||
{getIntensity(dare.intensity).label}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{dare.suggested_duration_hours ? (
|
||||
<Badge variant="outline" className="text-xs">
|
||||
|
||||
@@ -73,12 +73,8 @@ interface Question {
|
||||
config?: QuestionConfig | null;
|
||||
}
|
||||
|
||||
import { INTENSITY_LEVELS } from '@/lib/openai';
|
||||
|
||||
// Helper to get intensity info by level (1-indexed)
|
||||
const getIntensity = (level: number) => INTENSITY_LEVELS[level - 1] || INTENSITY_LEVELS[0];
|
||||
|
||||
const DEFAULT_AUDIO_DURATION = 60;
|
||||
const getDefaultIntensity = (isExplicit?: boolean) => (isExplicit ? 4 : 2);
|
||||
|
||||
const QUESTION_TYPE_OPTIONS: Array<{ value: QuestionType; label: string }> = [
|
||||
{ value: 'swipe', label: 'Swipe' },
|
||||
@@ -138,7 +134,7 @@ export function QuestionsPage() {
|
||||
}>({
|
||||
text: '',
|
||||
partner_text: '',
|
||||
intensity: 1,
|
||||
intensity: getDefaultIntensity(),
|
||||
allowed_couple_genders: [],
|
||||
target_user_genders: [],
|
||||
required_props: '',
|
||||
@@ -276,7 +272,7 @@ export function QuestionsPage() {
|
||||
setFormData({
|
||||
text: '',
|
||||
partner_text: '',
|
||||
intensity: 1,
|
||||
intensity: getDefaultIntensity(pack?.is_explicit),
|
||||
allowed_couple_genders: [],
|
||||
target_user_genders: [],
|
||||
required_props: '',
|
||||
@@ -388,6 +384,7 @@ export function QuestionsPage() {
|
||||
|
||||
const handleAiGenerated = (generatedQuestions: Array<{ text: string; partner_text?: string; intensity: number; inverse_pair_id?: string | null }>) => {
|
||||
const generatedQuestionType: QuestionType = 'swipe';
|
||||
const defaultIntensity = getDefaultIntensity(pack?.is_explicit);
|
||||
|
||||
// Bulk insert AI-generated questions with inverse_of linking
|
||||
const insertQuestions = async () => {
|
||||
@@ -414,7 +411,7 @@ export function QuestionsPage() {
|
||||
pack_id: packId,
|
||||
text: q.text,
|
||||
partner_text: q.partner_text || null,
|
||||
intensity: q.intensity,
|
||||
intensity: defaultIntensity,
|
||||
inverse_of: null,
|
||||
question_type: generatedQuestionType,
|
||||
config: null,
|
||||
@@ -434,7 +431,7 @@ export function QuestionsPage() {
|
||||
pack_id: packId,
|
||||
text: primary.text,
|
||||
partner_text: primary.partner_text || null,
|
||||
intensity: primary.intensity,
|
||||
intensity: defaultIntensity,
|
||||
inverse_of: null,
|
||||
question_type: generatedQuestionType,
|
||||
config: null,
|
||||
@@ -452,7 +449,7 @@ export function QuestionsPage() {
|
||||
pack_id: packId,
|
||||
text: inverse.text,
|
||||
partner_text: inverse.partner_text || null,
|
||||
intensity: inverse.intensity,
|
||||
intensity: defaultIntensity,
|
||||
inverse_of: primaryId,
|
||||
question_type: generatedQuestionType,
|
||||
config: null,
|
||||
@@ -470,7 +467,7 @@ export function QuestionsPage() {
|
||||
pack_id: packId,
|
||||
text: extra.text,
|
||||
partner_text: extra.partner_text || null,
|
||||
intensity: extra.intensity,
|
||||
intensity: defaultIntensity,
|
||||
inverse_of: null,
|
||||
question_type: generatedQuestionType,
|
||||
config: null,
|
||||
@@ -486,7 +483,7 @@ export function QuestionsPage() {
|
||||
pack_id: packId,
|
||||
text: pair[0].text,
|
||||
partner_text: pair[0].partner_text || null,
|
||||
intensity: pair[0].intensity,
|
||||
intensity: defaultIntensity,
|
||||
inverse_of: null,
|
||||
question_type: generatedQuestionType,
|
||||
config: null,
|
||||
@@ -732,25 +729,6 @@ export function QuestionsPage() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>Intensity Level</Label>
|
||||
<div className="flex gap-2">
|
||||
{INTENSITY_LEVELS.map((intensity) => (
|
||||
<Button
|
||||
key={intensity.level}
|
||||
type="button"
|
||||
variant={formData.intensity === intensity.level ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
onClick={() => setFormData(d => ({ ...d, intensity: intensity.level }))}
|
||||
className={formData.intensity === intensity.level ? intensity.color : ''}
|
||||
title={intensity.description}
|
||||
>
|
||||
{intensity.level} - {intensity.label}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="required_props">Required Props (optional)</Label>
|
||||
<Input
|
||||
@@ -1026,7 +1004,6 @@ export function QuestionsPage() {
|
||||
<TableHead className="w-32">Partner Text</TableHead>
|
||||
<TableHead className="w-24">Couples</TableHead>
|
||||
<TableHead className="w-24">Initiator</TableHead>
|
||||
<TableHead className="w-24">Intensity</TableHead>
|
||||
<TableHead className="w-24">Actions</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
@@ -1124,14 +1101,6 @@ export function QuestionsPage() {
|
||||
<span className="text-muted-foreground text-xs">Any</span>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge
|
||||
className={`${getIntensity(question.intensity).color} text-white`}
|
||||
title={getIntensity(question.intensity).description}
|
||||
>
|
||||
{getIntensity(question.intensity).label}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex gap-1">
|
||||
<Button
|
||||
|
||||
Reference in new issue
Block a user