a a vibe-coded abomination experiment of a fragrance review platform built on the atmosphere.
drydown.social
1/**
2 * Preference definitions for user fragrance settings.
3 *
4 * Each preference has 5 statement-based options that map to integer values 1-5.
5 * These preferences translate into scoring weights that personalize how reviews
6 * are evaluated. The underlying data model stores integers; these statements
7 * provide meaningful context for users.
8 */
9
10export interface PreferenceOption {
11 value: 1 | 2 | 3 | 4 | 5
12 statement: string
13}
14
15export interface PreferenceDefinition {
16 key: PreferenceKey
17 question: string
18 description: string
19 options: readonly [PreferenceOption, PreferenceOption, PreferenceOption, PreferenceOption, PreferenceOption]
20}
21
22export type PreferenceKey =
23 | 'presenceStyle'
24 | 'longevityPriority'
25 | 'complexityPreference'
26 | 'scoringApproach'
27
28export const PREFERENCE_DEFINITIONS: Record<PreferenceKey, PreferenceDefinition> = {
29 presenceStyle: {
30 key: 'presenceStyle',
31 question: 'How do you like to be noticed?',
32 description: 'This affects how much weight projection and sillage have in your scores.',
33 options: [
34 { value: 1, statement: 'Skin scent only — just for me' },
35 { value: 2, statement: 'Close company — noticeable only up close' },
36 { value: 3, statement: 'Balanced — depends on the occasion' },
37 { value: 4, statement: 'Room presence — I enjoy being noticed' },
38 { value: 5, statement: 'Announce myself — I like to make an entrance' },
39 ],
40 },
41
42 longevityPriority: {
43 key: 'longevityPriority',
44 question: 'How important is all-day longevity?',
45 description: 'This affects how much weight longevity and late-stage ratings have in your scores.',
46 options: [
47 { value: 1, statement: 'Not important — I enjoy reapplying throughout the day' },
48 { value: 2, statement: 'Nice to have — but not a dealbreaker' },
49 { value: 3, statement: 'Moderately important — I appreciate good lasting power' },
50 { value: 4, statement: 'Very important — I want it to last my workday' },
51 { value: 5, statement: 'Essential — all-day performance is a must' },
52 ],
53 },
54
55 complexityPreference: {
56 key: 'complexityPreference',
57 question: 'What kind of compositions appeal to you?',
58 description: 'This affects how much weight complexity has in your scores.',
59 options: [
60 { value: 1, statement: 'Simple and focused — I prefer clean, straightforward scents' },
61 { value: 2, statement: 'Mostly simple — with maybe a twist or two' },
62 { value: 3, statement: 'Balanced — I enjoy both simple and complex fragrances' },
63 { value: 4, statement: 'Layered — I like discovering new notes over time' },
64 { value: 5, statement: 'Intricate — the more depth and evolution, the better' },
65 ],
66 },
67
68 scoringApproach: {
69 key: 'scoringApproach',
70 question: 'How do you evaluate fragrances overall?',
71 description: 'This affects whether your gut feeling or technical ratings carry more weight.',
72 options: [
73 { value: 1, statement: 'Pure instinct — my gut reaction is what matters most' },
74 { value: 2, statement: 'Mostly instinct — but I consider the details too' },
75 { value: 3, statement: 'Balanced — both feeling and analysis matter equally' },
76 { value: 4, statement: 'Mostly analytical — I weigh each aspect carefully' },
77 { value: 5, statement: 'Fully analytical — the numbers tell the real story' },
78 ],
79 },
80} as const
81
82/** All preference keys in order */
83export const PREFERENCE_KEYS: readonly PreferenceKey[] = [
84 'presenceStyle',
85 'longevityPriority',
86 'complexityPreference',
87 'scoringApproach',
88] as const
89
90/** Default preference values (balanced/neutral) */
91export const DEFAULT_PREFERENCES: Record<PreferenceKey, number> = {
92 presenceStyle: 3,
93 longevityPriority: 5,
94 complexityPreference: 4,
95 scoringApproach: 4,
96}
97
98/**
99 * Base property importance rankings for ideal score calculation.
100 * Higher values = more important to overall score.
101 *
102 * Rankings (most to least important):
103 * 1. midProjection (1.5) - Heart projection matters most
104 * 2. openingProjection (1.45) - Initial projection
105 * 3. complexity (1.4) - Fragrance complexity
106 * 4. longevity (1.35) - Lasting power
107 * 5. sillage (1.3) - Trail/aura
108 * 6. drydownRating (1.25) - Heart quality
109 * 7. openingRating (1.2) - Opening quality
110 * 8. endRating (1.15) - Base quality
111 * 9. overallRating (1.1) - Gut check (least important)
112 */
113export const BASE_PROPERTY_IMPORTANCE = {
114 midProjection: 1.5,
115 openingProjection: 1.45,
116 complexity: 1.4,
117 longevity: 1.35,
118 sillage: 1.3,
119 drydownRating: 1.25,
120 openingRating: 1.2,
121 endRating: 1.15,
122 overallRating: 1.1,
123} as const
124
125/**
126 * Ideal scores derived from user preferences.
127 * Represents the user's "perfect" ratings for each property.
128 */
129export interface IdealScores {
130 // Quality ratings (always 5 - everyone wants best quality)
131 openingRating: 5
132 drydownRating: 5
133 endRating: 5
134 overallRating: 5
135
136 // Performance (from presenceStyle)
137 openingProjection: number // = presenceStyle (1-5)
138 midProjection: number // = presenceStyle (1-5)
139 sillage: number // = presenceStyle (1-5)
140
141 // Longevity (from longevityPriority)
142 longevity: number // = longevityPriority (1-5)
143
144 // Complexity (from complexityPreference)
145 complexity: number // = complexityPreference (1-5)
146}
147
148/**
149 * Property importance values (base + adjustments from scoringApproach).
150 */
151export interface PropertyImportance {
152 openingRating: number
153 openingProjection: number
154 drydownRating: number
155 midProjection: number
156 sillage: number
157 endRating: number
158 complexity: number
159 longevity: number
160 overallRating: number
161}