@baravak/risloo-profile-cli 4.70.0 → 4.77.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1021,7 +1021,7 @@
1021
1021
  {
1022
1022
  "type": "text",
1023
1023
  "label": "marital_status",
1024
- "text": "جنسیت",
1024
+ "text": "وضعیت تأهل",
1025
1025
  "answer": { "type": "select", "options": ["مجرد", "متأهل"] },
1026
1026
  "alias": "marital_status",
1027
1027
  "force": true,
@@ -0,0 +1,530 @@
1
+ const { Profile } = require("../Profile");
2
+
3
+ const INSIDE_THRESHOLD = 25;
4
+ const MAIN_INSIDE_SCORE_GAP = 2;
5
+ const FACET_INSIDE_SCORE_GAP = 6;
6
+ const OUTSIDE_SCORE_GAP = 6;
7
+
8
+ const PERSONALITY = [
9
+ { key: "1", code: "1", fa: "اسکیزوئید" },
10
+ { key: "2a", code: "2A", fa: "اجتنابی" },
11
+ { key: "2b", code: "2B", fa: "افسرده" },
12
+ { key: "3", code: "3", fa: "وابسته" },
13
+ { key: "4a", code: "4A", fa: "نمایشی" },
14
+ { key: "4b", code: "4B", fa: "آشفته" },
15
+ { key: "5", code: "5", fa: "خودشیفته" },
16
+ { key: "6a", code: "6A", fa: "ضداجتماعی" },
17
+ { key: "6b", code: "6B", fa: "دیگرآزار" },
18
+ { key: "7", code: "7", fa: "جبری" },
19
+ { key: "8a", code: "8A", fa: "منفی‌گرا" },
20
+ { key: "8b", code: "8B", fa: "خودآزار" },
21
+ { key: "s", code: "S", fa: "اسکیزوتایپال" },
22
+ { key: "c", code: "C", fa: "مرزی" },
23
+ { key: "p", code: "P", fa: "پارانوئید" },
24
+ ];
25
+
26
+ const CLINICAL = [
27
+ { key: "a", code: "A", fa: "اضطراب تعمیم‌یافته" },
28
+ { key: "h", code: "H", fa: "علائم جسمانی" },
29
+ { key: "n", code: "N", fa: "طیف دوقطبی" },
30
+ { key: "d", code: "D", fa: "افسردگی مداوم" },
31
+ { key: "b", code: "B", fa: "مصرف الکل" },
32
+ { key: "t", code: "T", fa: "مصرف مواد" },
33
+ { key: "r", code: "R", fa: "استرس پس از ضربه" },
34
+ { key: "ss", code: "SS", fa: "طیف اسکیزوفرنیا" },
35
+ { key: "cc", code: "CC", fa: "افسردگی اساسی" },
36
+ { key: "pp", code: "PP", fa: "اختلال هذیانی" },
37
+ ];
38
+
39
+ const MODIFYING = [
40
+ { key: "x", code: "X", fa: "افشا" },
41
+ { key: "y", code: "Y", fa: "مطلوبیت" },
42
+ { key: "z", code: "Z", fa: "منفی‌نمایی" },
43
+ ];
44
+
45
+ const FACET_NAMES = {
46
+ 1: ["غیرتعاملی", "از نظر درونی کم‌مایه", "از نظر خلق و خوی بی‌احساس"],
47
+ "2a": ["رویگردان از روابط بین‌فردی", "خودپنداره احساس عدم تعلق", "از نظر درونی آزرده"],
48
+ "2b": ["از نظر شناختی جبرگرا", "خود بی‌ارزش پنداری", "از نظر خلق ماتم‌زده"],
49
+ 3: ["نمودهای نابالغانه رفتاری", "از نظر بین‌فردی مطیع", "بی‌عرضه‌پنداری خود"],
50
+ "4a": ["به‌طور چشم‌گیری نمایشی", "از نظر بین‌فردی در جست‌وجوی توجه", "از نظر خلق ناپایدار"],
51
+ "4b": ["به وضوح تند و شتابزده", "از نظر بین‌فردی بی‌پروا", "خودبزرگ‌پنداری"],
52
+ 5: ["از نظر بین‌فردی استثمارگر", "از نظر ذهنی مهار نشده", "تصویری ستودنی از خود"],
53
+ "6a": ["مسئولیت‌ناپذیر در روابط اجتماعی", "خودمختار پنداری", "برون‌ریزی شدید"],
54
+ "6b": ["بیان تند و آزارنده", "کینه‌توز در روابط بین‌فردی", "منش انفجاری"],
55
+ 7: ["شدیداً منضبط", "از نظر شناختی خشک و ریزبین", "مطمئن به خود"],
56
+ "8a": ["بسیار تلخ و گزنده", "ناراضی از خود", "از نظر خلقی تحریک‌پذیر"],
57
+ "8b": ["خود نالایق‌پنداری", "منش متضاد (در تجربه لذت و درد)", "از نظر خلقی ملول"],
58
+ s: ["از نظر شناختی غیرعادی", "خودپنداره عجیب‌غریب", "از نظر درونی درهم"],
59
+ c: ["خودپنداره ناپایدار", "از نظر ساختار روانی دونیمه", "خلق بی‌ثبات"],
60
+ p: ["داشتن حالت تدافعی", "از نظر شناختی بی‌اعتماد", "فرافکنی مداوم"],
61
+ };
62
+
63
+ const NOTEWORTHY = [
64
+ { key: "ad", code: "AD", fa: "اختلال نقص‌توجه بیش‌فعالی بزرگسالان" },
65
+ { key: "as", code: "AS", fa: "طیف اتیسم" },
66
+ { key: "ca", code: "CA", fa: "سوءاستفاده کودکی" },
67
+ { key: "ea", code: "EA", fa: "اختلالات خوردن" },
68
+ { key: "em", code: "EM", fa: "عدم کنترل هیجانی" },
69
+ { key: "ex", code: "EX", fa: "خشم انفجاری" },
70
+ { key: "hp", code: "HP", fa: "دل‌مشغولی سلامتی" },
71
+ { key: "ia", code: "IA", fa: "بیگانگی با دیگران" },
72
+ { key: "pd", code: "PD", fa: "سوءمصرف داروهای تجویزی" },
73
+ { key: "sp", code: "SP", fa: "پتانسیل خود تخریبی" },
74
+ { key: "sb", code: "SB", fa: "رفتارها / تمایلات خودآسیبی" },
75
+ { key: "tb", code: "TB", fa: "آسیب تروماتیک مغز" },
76
+ { key: "vp", code: "VP", fa: "تمایل به انتقام جویی" },
77
+ ];
78
+
79
+ const THEMES = {
80
+ blue: {
81
+ gradient: "mcmBlue",
82
+ track: "#EFF6FF",
83
+ light: "#93C5FD",
84
+ dark: "#2563EB",
85
+ text: "#1E40AF",
86
+ cardText: "#1E40AF",
87
+ card: "#EFF6FF",
88
+ stroke: "#BFDBFE",
89
+ divider: "#DBEAFE",
90
+ },
91
+ gray: {
92
+ gradient: "mcmGray",
93
+ track: "#F8FAFC",
94
+ light: "#E2E8F0",
95
+ dark: "#475569",
96
+ text: "#475569",
97
+ cardText: "#475569",
98
+ card: "#F8FAFC",
99
+ stroke: "#E2E8F0",
100
+ divider: "#F1F5F9",
101
+ },
102
+ zinc: {
103
+ gradient: "mcmZinc",
104
+ track: "#FAFAFA",
105
+ dark: "#475569",
106
+ text: "#475569",
107
+ },
108
+ yellow: {
109
+ gradient: "mcmYellow",
110
+ track: "#FEFCE8",
111
+ light: "#FEF08A",
112
+ dark: "#EAB308",
113
+ text: "#EAB308",
114
+ cardText: "#EAB308",
115
+ card: "#FEFCE8",
116
+ stroke: "#FEF08A",
117
+ divider: "#FEF9C3",
118
+ },
119
+ orange: {
120
+ gradient: "mcmOrange",
121
+ track: "#FFF7ED",
122
+ light: "#FDBA74",
123
+ dark: "#EA580C",
124
+ text: "#EA580C",
125
+ cardText: "#EA580C",
126
+ card: "#FFF7ED",
127
+ stroke: "#FED7AA",
128
+ divider: "#FFEDD5",
129
+ },
130
+ rose: {
131
+ gradient: "mcmRose",
132
+ track: "#FFF1F2",
133
+ light: "#FDA4AF",
134
+ dark: "#E11D48",
135
+ text: "#E11D48",
136
+ cardText: "#BE123C",
137
+ card: "#FFF1F2",
138
+ stroke: "#FECDD3",
139
+ divider: "#FEE2E2",
140
+ },
141
+ };
142
+
143
+ const REPORTS = {
144
+ valid: {
145
+ text: "معتبر",
146
+ background: "#ECFDF5",
147
+ color: "#059669",
148
+ rawY: 54.2,
149
+ equalsY: 71,
150
+ reportY: 96.8,
151
+ },
152
+ questionable: {
153
+ text: "تفسیر با احتیاط",
154
+ background: "#FEFCE8",
155
+ color: "#CA8A04",
156
+ rawY: 27.1,
157
+ equalsY: 49.5,
158
+ reportY: 102,
159
+ },
160
+ invalid: {
161
+ text: "نامعتبر",
162
+ background: "#FEF2F2",
163
+ color: "#DC2626",
164
+ rawY: 50.9,
165
+ equalsY: 70,
166
+ reportY: 99.8,
167
+ },
168
+ };
169
+
170
+ const CORRECTIONS = {
171
+ none: {
172
+ text: "خیر",
173
+ background: "#FEF2F2",
174
+ color: "#DC2626",
175
+ bodyHeight: 68,
176
+ valid: false,
177
+ },
178
+ x: {
179
+ text: "شاخص افشا (X)",
180
+ background: "#ECFDF5",
181
+ color: "#059669",
182
+ bodyHeight: 131,
183
+ valid: true,
184
+ },
185
+ acc: {
186
+ text: "شاخص اضطراب/افسردگی (A/CC)",
187
+ background: "#ECFDF5",
188
+ color: "#059669",
189
+ bodyHeight: 211,
190
+ valid: true,
191
+ },
192
+ x_acc: {
193
+ text: "شاخص X و A/CC",
194
+ background: "#ECFDF5",
195
+ color: "#059669",
196
+ bodyHeight: 137,
197
+ valid: true,
198
+ },
199
+ };
200
+
201
+ const PROFILE_LABELS = {};
202
+ const addLabel = (eng) => {
203
+ PROFILE_LABELS[eng] = { eng };
204
+ };
205
+
206
+ for (const { key } of [...PERSONALITY, ...CLINICAL]) {
207
+ for (const suffix of ["raw", "br", "level"]) addLabel(`${key}_${suffix}`);
208
+ }
209
+ for (const key of Object.keys(FACET_NAMES)) {
210
+ for (let index = 1; index <= 3; index += 1) {
211
+ for (const suffix of ["raw", "br", "level"]) addLabel(`${key}_${index}_${suffix}`);
212
+ }
213
+ }
214
+ for (const { key } of MODIFYING) {
215
+ for (const suffix of ["raw", "br", "report", "level"]) addLabel(`${key}_${suffix}`);
216
+ }
217
+ for (const key of ["v", "w"]) {
218
+ addLabel(`${key}_raw`);
219
+ addLabel(`${key}_report`);
220
+ }
221
+ for (const eng of [
222
+ "high_point",
223
+ "validity_alert",
224
+ "validity_reason",
225
+ "correction_alert",
226
+ "correction_x_1",
227
+ "correction_x_2",
228
+ "correction_acc_value",
229
+ "correction_acc_1",
230
+ "correction_acc_2",
231
+ ]) {
232
+ addLabel(eng);
233
+ }
234
+ for (const { key } of NOTEWORTHY) {
235
+ addLabel(`noteworthy_${key}_raw`);
236
+ addLabel(`noteworthy_${key}_percentage`);
237
+ }
238
+
239
+ function clamp(value, min, max) {
240
+ const number = Number(value);
241
+ if (!Number.isFinite(number)) return min;
242
+ return Math.min(max, Math.max(min, number));
243
+ }
244
+
245
+ function brHeight(value) {
246
+ const br = clamp(value, 0, 115);
247
+ if (br <= 60) return br * 1.5;
248
+ if (br <= 75) return 90 + (br - 60) * 7;
249
+ if (br <= 85) return 195 + (br - 75) * 10;
250
+ return 295 + (br - 85) * 3.5;
251
+ }
252
+
253
+ function personalityTheme(value) {
254
+ const br = clamp(value, 0, 115);
255
+ if (br < 60) return THEMES.gray;
256
+ if (br < 75) return THEMES.yellow;
257
+ if (br < 85) return THEMES.orange;
258
+ return THEMES.rose;
259
+ }
260
+
261
+ function clinicalTheme(value) {
262
+ const br = clamp(value, 0, 115);
263
+ if (br < 75) return THEMES.gray;
264
+ if (br < 85) return THEMES.orange;
265
+ return THEMES.rose;
266
+ }
267
+
268
+ class MCMI9A extends Profile {
269
+ static pages = 4;
270
+
271
+ static partials = {
272
+ MCMI9A_main: "MCMI9A_main_partial.hbs",
273
+ MCMI9A_facets: "MCMI9A_facets_partial.hbs",
274
+ MCMI9A_noteworthy: "MCMI9A_noteworthy_partial.hbs",
275
+ MCMI9A_vertical_label: "MCMI9A_vertical_label_partial.hbs",
276
+ };
277
+
278
+ labels = PROFILE_LABELS;
279
+
280
+ profileSpec = {
281
+ sample: {
282
+ name: "آزمون شخصیت میلون IV",
283
+ multiProfile: false,
284
+ questions: false,
285
+ defaultFields: true,
286
+ fields: [],
287
+ },
288
+ profile: {
289
+ get dimensions() {
290
+ const [page1, page2, page3, page4] = this.padding;
291
+ return [
292
+ {
293
+ width: 888 + 2 * page1.x,
294
+ height: 706 + 2 * page1.y,
295
+ },
296
+ {
297
+ width: 858 + 2 * page2.x,
298
+ height: 699 + 2 * page2.y,
299
+ },
300
+ {
301
+ width: 858 + 2 * page3.x,
302
+ height: 699 + 2 * page3.y,
303
+ },
304
+ {
305
+ width: 737 + 2 * page4.x,
306
+ height: 558 + 2 * page4.y,
307
+ },
308
+ ];
309
+ },
310
+ padding: [
311
+ { x: 7.5, y: 4 },
312
+ { x: 22.5, y: 7.5 },
313
+ { x: 22.5, y: 7.5 },
314
+ { x: 83, y: 78 },
315
+ ],
316
+ },
317
+ labels: Object.values(this.labels),
318
+ };
319
+
320
+ constructor(dataset, options, config = {}) {
321
+ super();
322
+ this._init(dataset, options, config);
323
+ }
324
+
325
+ _calcContext() {
326
+ const scoreByKey = new Map(this.dataset.score.map((item) => [item.label.eng, item]));
327
+ const mark = (eng) => scoreByKey.get(eng)?.mark ?? 0;
328
+ const number = (eng) => {
329
+ const value = Number(mark(eng));
330
+ return Number.isFinite(value) ? value : 0;
331
+ };
332
+ const titleAppend = (page) => ` - صفحه ${page}`;
333
+
334
+ const modifying = MODIFYING.map((item, index) => {
335
+ const br = clamp(number(`${item.key}_br`), 0, 100);
336
+ const barHeight = br * 4;
337
+ const inside = br >= INSIDE_THRESHOLD;
338
+ const barTop = 464 - barHeight;
339
+ return {
340
+ ...item,
341
+ x: 144 + index * 20,
342
+ center: 150 + index * 20,
343
+ raw: number(`${item.key}_raw`),
344
+ br,
345
+ barHeight,
346
+ barTop,
347
+ scoreY: inside ? barTop + MAIN_INSIDE_SCORE_GAP : barTop - OUTSIDE_SCORE_GAP,
348
+ scoreAnchor: inside ? "end" : "start",
349
+ inside,
350
+ theme: THEMES.blue,
351
+ };
352
+ });
353
+
354
+ const personality = PERSONALITY.map((item, index) => {
355
+ const br = clamp(number(`${item.key}_br`), 0, 115);
356
+ const barHeight = brHeight(br);
357
+ const x = index < 12 ? 276 + index * 20 : 532 + (index - 12) * 20;
358
+ const inside = br >= 60;
359
+ const barTop = 464 - barHeight;
360
+ return {
361
+ ...item,
362
+ x,
363
+ center: x + 6,
364
+ raw: number(`${item.key}_raw`),
365
+ br,
366
+ level: mark(`${item.key}_level`),
367
+ barHeight,
368
+ barTop,
369
+ scoreY: inside ? barTop + MAIN_INSIDE_SCORE_GAP : barTop - OUTSIDE_SCORE_GAP,
370
+ scoreAnchor: inside ? "end" : "start",
371
+ inside,
372
+ theme: personalityTheme(br),
373
+ };
374
+ });
375
+
376
+ const clinical = CLINICAL.map((item, index) => {
377
+ const br = clamp(number(`${item.key}_br`), 0, 115);
378
+ const barHeight = brHeight(br);
379
+ const x = index < 7 ? 672 + index * 20 : 828 + (index - 7) * 20;
380
+ const inside = br >= 75;
381
+ const barTop = 464 - barHeight;
382
+ return {
383
+ ...item,
384
+ x,
385
+ center: x + 6,
386
+ raw: number(`${item.key}_raw`),
387
+ br,
388
+ level: mark(`${item.key}_level`),
389
+ barHeight,
390
+ barTop,
391
+ scoreY: inside ? barTop + MAIN_INSIDE_SCORE_GAP : barTop - OUTSIDE_SCORE_GAP,
392
+ scoreAnchor: inside ? "end" : "start",
393
+ inside,
394
+ theme: clinicalTheme(br),
395
+ };
396
+ });
397
+
398
+ const facetGroups = (order, forcedGrayKeys) =>
399
+ order.map((key, groupIndex) => {
400
+ const parent = PERSONALITY.find((item) => item.key === key);
401
+ const br = clamp(number(`${key}_br`), 0, 115);
402
+ const forcedGray = forcedGrayKeys.has(key);
403
+ const theme = forcedGray ? THEMES.gray : personalityTheme(br);
404
+ const baseX = 42 + groupIndex * 104;
405
+ const facets = FACET_NAMES[key].map((fa, facetIndex) => {
406
+ const facetKey = `${key}_${facetIndex + 1}`;
407
+ const facetBr = clamp(number(`${facetKey}_br`), 0, 100);
408
+ const barHeight = facetBr * 3;
409
+ const inside = facetBr >= 75;
410
+ const facetTheme = forcedGray
411
+ ? THEMES.gray
412
+ : inside
413
+ ? theme
414
+ : THEMES.zinc;
415
+ const barTop = 361 - barHeight;
416
+ return {
417
+ key: facetKey,
418
+ code: `${parent.code}-${facetIndex + 1}`,
419
+ fa,
420
+ x: baseX + facetIndex * 28,
421
+ center: baseX + facetIndex * 28 + 8,
422
+ raw: number(`${facetKey}_raw`),
423
+ br: facetBr,
424
+ level: mark(`${facetKey}_level`),
425
+ barHeight,
426
+ barTop,
427
+ scoreY: inside ? barTop + FACET_INSIDE_SCORE_GAP : barTop - OUTSIDE_SCORE_GAP,
428
+ scoreAnchor: inside ? "end" : "start",
429
+ inside,
430
+ theme: facetTheme,
431
+ };
432
+ });
433
+
434
+ return {
435
+ ...parent,
436
+ cardLines: key === "6b" ? ["دیگرآزار", "(سادیستیک)"] : [parent.fa],
437
+ baseX,
438
+ cardX: baseX - 3.5,
439
+ raw: number(`${key}_raw`),
440
+ br,
441
+ level: mark(`${key}_level`),
442
+ theme,
443
+ facets,
444
+ };
445
+ });
446
+
447
+ const sortedFacetKeys = PERSONALITY.map((item, index) => ({
448
+ key: item.key,
449
+ index,
450
+ br: clamp(number(`${item.key}_br`), 0, 115),
451
+ }))
452
+ .sort((first, second) => second.br - first.br || first.index - second.index)
453
+ .map((item) => item.key);
454
+ const forcedGrayKeys = new Set(sortedFacetKeys.slice(-4));
455
+
456
+ const noteworthy = NOTEWORTHY.map((item, index) => {
457
+ const raw = number(`noteworthy_${item.key}_raw`);
458
+ const ratio = clamp(number(`noteworthy_${item.key}_percentage`), 0, 1);
459
+ const percentage = Math.round(ratio * 100);
460
+ const barWidth = percentage * 4;
461
+ return {
462
+ ...item,
463
+ index,
464
+ rowY: 30 + index * 40,
465
+ barY: 42 + index * 40,
466
+ centerY: 50 + index * 40,
467
+ raw,
468
+ ratio,
469
+ percentage,
470
+ barWidth,
471
+ inside: percentage >= INSIDE_THRESHOLD,
472
+ scoreX: 273 + barWidth + (percentage >= INSIDE_THRESHOLD ? -6 : 6),
473
+ };
474
+ });
475
+
476
+ const correctionKey = String(mark("correction_alert") || "none");
477
+ const correction = CORRECTIONS[correctionKey] ?? CORRECTIONS.none;
478
+ const validity = (key) => {
479
+ const reportKey = String(mark(`${key}_report`) || "valid");
480
+ return {
481
+ raw: number(`${key}_raw`),
482
+ ...(REPORTS[reportKey] ?? REPORTS.valid),
483
+ };
484
+ };
485
+ const highPoints = [...personality, ...clinical]
486
+ .map((item, index) => ({ ...item, highPointOrder: index }))
487
+ .filter((item) => item.br >= 75)
488
+ .sort(
489
+ (first, second) =>
490
+ second.br - first.br || first.highPointOrder - second.highPointOrder,
491
+ )
492
+ .map((item) => item.code)
493
+ .slice(0, 5);
494
+ const disclosureRaw = number("x_raw");
495
+ const showDisclosureAlert =
496
+ disclosureRaw <= 6 || (disclosureRaw >= 115 && disclosureRaw <= 121);
497
+
498
+ return [
499
+ {
500
+ page: 1,
501
+ titleAppend: titleAppend(1),
502
+ highPoints,
503
+ showDisclosureAlert,
504
+ correction,
505
+ validityV: validity("v"),
506
+ validityW: validity("w"),
507
+ modifying,
508
+ personality,
509
+ clinical,
510
+ },
511
+ {
512
+ page: 2,
513
+ titleAppend: titleAppend(2) + ' - گراسمن ۱',
514
+ groups: facetGroups(sortedFacetKeys.slice(0, 8), forcedGrayKeys),
515
+ },
516
+ {
517
+ page: 3,
518
+ titleAppend: titleAppend(3)+ ' - گراسمن ۲',
519
+ groups: facetGroups(sortedFacetKeys.slice(8), forcedGrayKeys),
520
+ },
521
+ {
522
+ page: 4,
523
+ titleAppend: titleAppend(4) + ' - شاخص‌های هشداردهنده',
524
+ items: noteworthy,
525
+ },
526
+ ];
527
+ }
528
+ }
529
+
530
+ module.exports = MCMI9A;
@@ -2,7 +2,7 @@ const { Profile } = require("../Profile");
2
2
 
3
3
  const DOMAIN_MAX = 192;
4
4
  const FACET_MAX = 32;
5
- const INSIDE_THRESHOLD = 24;
5
+ const INSIDE_THRESHOLD = 25;
6
6
 
7
7
  const LEVELS = ["", "خیلی پایین", "پایین", "متوسط", "بالا", "خیلی بالا"];
8
8
 
@@ -146,6 +146,8 @@ class NEO93 extends Profile {
146
146
  const isFemale = Number(genderField?.user_answered) === 1;
147
147
  const gender = isFemale ? "female" : "male";
148
148
  const colors = COLOR_THEMES[gender];
149
+ const versionTitle = isFemale ? "نسخه بانوان" : "نسخه آقایان";
150
+ const titleAppend = (page) => ` - ${versionTitle} - صفحه ${page}`;
149
151
  const sharedContext = {
150
152
  gender,
151
153
  isFemale,
@@ -249,7 +251,7 @@ class NEO93 extends Profile {
249
251
  {
250
252
  ...sharedContext,
251
253
  page: 1,
252
- titleAppend: "",
254
+ titleAppend: titleAppend(1),
253
255
  items,
254
256
  redErrors: layoutIndicators(redErrors),
255
257
  yellowErrors: layoutIndicators(yellowErrors),
@@ -259,14 +261,14 @@ class NEO93 extends Profile {
259
261
  {
260
262
  ...sharedContext,
261
263
  page: 2,
262
- titleAppend: " - 2",
264
+ titleAppend: titleAppend(2),
263
265
  blocks: blocks.slice(0, 3),
264
266
  gridBottom: 658.5,
265
267
  },
266
268
  {
267
269
  ...sharedContext,
268
270
  page: 3,
269
- titleAppend: " - 3",
271
+ titleAppend: titleAppend(3),
270
272
  blocks: blocks.slice(3, 5),
271
273
  gridBottom: 454.5,
272
274
  },
@@ -2,6 +2,7 @@ const { Profile } = require("../Profile");
2
2
 
3
3
  const DOMAIN_MAX = 48;
4
4
  const BAR_WIDTH = 500;
5
+ const INSIDE_THRESHOLD = 25;
5
6
  const LEVELS = {
6
7
  1: "خیلی پایین",
7
8
  2: "پایین",
@@ -96,6 +97,7 @@ class NEO9A extends Profile {
96
97
  p,
97
98
  barW: (BAR_WIDTH * percentage) / 100,
98
99
  percentage,
100
+ inside: percentage >= INSIDE_THRESHOLD,
99
101
  levelText: LEVELS[level] ?? "-",
100
102
  };
101
103
  });
@@ -108,7 +110,7 @@ class NEO9A extends Profile {
108
110
  return [
109
111
  {
110
112
  page: 1,
111
- titleAppend: "",
113
+ titleAppend: " - صفحه 1",
112
114
  theme: "sky",
113
115
  colors: COLORS,
114
116
  items,
@@ -2,7 +2,7 @@ const { Profile } = require("../Profile");
2
2
 
3
3
  const DOMAIN_MAX = 192;
4
4
  const FACET_MAX = 32;
5
- const INSIDE_THRESHOLD = 24;
5
+ const INSIDE_THRESHOLD = 25;
6
6
 
7
7
  const LEVELS = ["", "بسیار پایین", "پایین", "طبیعی", "بالا", "بسیار بالا"];
8
8
 
@@ -145,6 +145,8 @@ class NEO9Q extends Profile {
145
145
  const isFemale = Number(genderField?.user_answered) === 1;
146
146
  const gender = isFemale ? "female" : "male";
147
147
  const colors = COLOR_THEMES[gender];
148
+ const versionTitle = isFemale ? "نسخه بانوان" : "نسخه آقایان";
149
+ const titleAppend = (page) => ` - ${versionTitle} - صفحه ${page}`;
148
150
  const sharedContext = {
149
151
  gender,
150
152
  isFemale,
@@ -251,7 +253,7 @@ class NEO9Q extends Profile {
251
253
  {
252
254
  ...sharedContext,
253
255
  page: 1,
254
- titleAppend: "",
256
+ titleAppend: titleAppend(1),
255
257
  items,
256
258
  redErrors: layoutIndicators(redErrors),
257
259
  yellowErrors: layoutIndicators(yellowErrors),
@@ -261,14 +263,14 @@ class NEO9Q extends Profile {
261
263
  {
262
264
  ...sharedContext,
263
265
  page: 2,
264
- titleAppend: " - 2",
266
+ titleAppend: titleAppend(2),
265
267
  blocks: blocks.slice(0, 3),
266
268
  gridBottom: 664.5,
267
269
  },
268
270
  {
269
271
  ...sharedContext,
270
272
  page: 3,
271
- titleAppend: " - 3",
273
+ titleAppend: titleAppend(3),
272
274
  blocks: blocks.slice(3, 5),
273
275
  gridBottom: 460.5,
274
276
  },
@@ -3,10 +3,11 @@ const { Profile } = require("../Profile");
3
3
  const DOMAIN_MAX = 48;
4
4
  const MAIN_BAR_WIDTH = 500;
5
5
  const DETAIL_BAR_WIDTH = 300;
6
+ const INSIDE_THRESHOLD = 25;
6
7
  const COLORS = {
7
- light: "#BFDBFE",
8
- dark: "#1D4ED8",
9
- accent: "#2563EB",
8
+ light: "#BAE6FD",
9
+ dark: "#0369A1",
10
+ accent: "#0284C7",
10
11
  stroke: "#60A5FA",
11
12
  };
12
13
  const percentageFromRatio = (ratio) => Math.round(Number(ratio) * 100);
@@ -164,6 +165,7 @@ class NEO9V extends Profile {
164
165
  p: facetP,
165
166
  barW: (DETAIL_BAR_WIDTH * facetPercentage) / 100,
166
167
  percentage: facetPercentage,
168
+ inside: facetPercentage >= INSIDE_THRESHOLD,
167
169
  };
168
170
  });
169
171
 
@@ -176,6 +178,7 @@ class NEO9V extends Profile {
176
178
  p,
177
179
  barW: (MAIN_BAR_WIDTH * percentage) / 100,
178
180
  percentage,
181
+ inside: percentage >= INSIDE_THRESHOLD,
179
182
  isBlue: index % 2 === 0,
180
183
  facets,
181
184
  };
@@ -193,7 +196,7 @@ class NEO9V extends Profile {
193
196
  return [
194
197
  {
195
198
  page: 1,
196
- titleAppend: "",
199
+ titleAppend: " - صفحه 1",
197
200
  theme: "blue",
198
201
  colors: COLORS,
199
202
  items: blocks.map(({ facets, isBlue, ...item }) => item),
@@ -202,7 +205,7 @@ class NEO9V extends Profile {
202
205
  },
203
206
  {
204
207
  page: 2,
205
- titleAppend: " - 2",
208
+ titleAppend: " - صفحه 2",
206
209
  theme: "blue",
207
210
  colors: COLORS,
208
211
  blocks,