From 8f2b2c34ffc2caf36a397b4945f07dcb88562e4a Mon Sep 17 00:00:00 2001
From: pluja
Date: Sun, 25 May 2025 11:21:35 +0000
Subject: [PATCH] Release 2025-05-25-irZj
---
.cursor/rules/database.mdc | 1 +
.env.example | 0
web/package.json | 2 +-
.../20250525101939_archieved/migration.sql | 2 +
web/prisma/schema.prisma | 1 +
web/scripts/faker.ts | 20 ++-
web/src/components/CommentItem.astro | 2 +-
web/src/components/ServiceCard.astro | 31 ++++-
web/src/constants/serviceVisibility.ts | 15 +++
web/src/lib/attributes.ts | 12 ++
.../pages/admin/services/[slug]/edit.astro | 125 ++++++++++++------
web/src/pages/admin/services/index.astro | 19 +--
web/src/pages/index.astro | 5 +-
web/src/pages/service/[slug].astro | 24 ++--
web/src/styles/global.css | 4 +
15 files changed, 194 insertions(+), 69 deletions(-)
create mode 100644 .env.example
create mode 100644 web/prisma/migrations/20250525101939_archieved/migration.sql
diff --git a/.cursor/rules/database.mdc b/.cursor/rules/database.mdc
index a9bda76..0bca92b 100644
--- a/.cursor/rules/database.mdc
+++ b/.cursor/rules/database.mdc
@@ -5,6 +5,7 @@ alwaysApply: false
---
- We use Prisma as ORM.
- Remember to check the prisma schema [schema.prisma](mdc:web/prisma/schema.prisma) when doing things related to the database.
+- After making changes to the [schema.prisma](mdc:web/prisma/schema.prisma) database or [faker.ts](mdc:web/scripts/faker.ts), you can run `npm run db-reset` (from `/web/` folder) [package.json](mdc:web/package.json).
- Import the types from prisma instead of hardcoding duplicates. Specially use the Prisma.___GetPayload type and the enums. Like this:
```ts
type Props = {
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..e69de29
diff --git a/web/package.json b/web/package.json
index 180a113..355ba84 100644
--- a/web/package.json
+++ b/web/package.json
@@ -12,7 +12,7 @@
"db-push": "prisma migrate dev",
"db-triggers": "just import-triggers",
"db-update": "prisma migrate dev && just import-triggers",
- "db-reset": "prisma migrate reset && prisma migrate dev && just import-triggers && tsx scripts/faker.ts",
+ "db-reset": "prisma migrate reset -f && prisma migrate dev && just import-triggers && tsx scripts/faker.ts",
"db-fill": "tsx scripts/faker.ts",
"db-fill-clean": "tsx scripts/faker.ts --cleanup",
"format": "prettier --write .",
diff --git a/web/prisma/migrations/20250525101939_archieved/migration.sql b/web/prisma/migrations/20250525101939_archieved/migration.sql
new file mode 100644
index 0000000..0fc8b63
--- /dev/null
+++ b/web/prisma/migrations/20250525101939_archieved/migration.sql
@@ -0,0 +1,2 @@
+-- AlterEnum
+ALTER TYPE "ServiceVisibility" ADD VALUE 'ARCHIVED';
diff --git a/web/prisma/schema.prisma b/web/prisma/schema.prisma
index eb17ec9..c5f719a 100644
--- a/web/prisma/schema.prisma
+++ b/web/prisma/schema.prisma
@@ -87,6 +87,7 @@ enum ServiceVisibility {
PUBLIC
UNLISTED
HIDDEN
+ ARCHIVED
}
enum Currency {
diff --git a/web/scripts/faker.ts b/web/scripts/faker.ts
index 20937de..f13c83f 100755
--- a/web/scripts/faker.ts
+++ b/web/scripts/faker.ts
@@ -2,19 +2,20 @@ import crypto from 'crypto'
import { faker } from '@faker-js/faker'
import {
+ AnnouncementType,
AttributeCategory,
AttributeType,
CommentStatus,
Currency,
+ EventType,
PrismaClient,
ServiceSuggestionStatus,
ServiceSuggestionType,
+ ServiceUserRole,
VerificationStatus,
type Prisma,
- EventType,
type User,
- ServiceUserRole,
- AnnouncementType,
+ type ServiceVisibility,
} from '@prisma/client'
import { uniqBy } from 'lodash-es'
import { generateUsername } from 'unique-username-generator'
@@ -611,7 +612,12 @@ const generateFakeEvent = (serviceId: number) => {
}
const generateFakeService = (users: User[]) => {
- const status = faker.helpers.arrayElement(Object.values(VerificationStatus))
+ const status = faker.helpers.weightedArrayElement([
+ { weight: 20, value: 'VERIFICATION_SUCCESS' },
+ { weight: 30, value: 'APPROVED' },
+ { weight: 40, value: 'COMMUNITY_CONTRIBUTED' },
+ { weight: 10, value: 'VERIFICATION_FAILED' },
+ ])
const name = faker.helpers.arrayElement(serviceNames)
const slug = `${faker.helpers.slugify(name).toLowerCase()}-${faker.string.alphanumeric({ length: 6, casing: 'lower' })}`
@@ -623,6 +629,12 @@ const generateFakeService = (users: User[]) => {
overallScore: 0,
privacyScore: 0,
trustScore: 0,
+ serviceVisibility: faker.helpers.weightedArrayElement([
+ { weight: 80, value: 'PUBLIC' },
+ { weight: 10, value: 'UNLISTED' },
+ { weight: 5, value: 'HIDDEN' },
+ { weight: 5, value: 'ARCHIVED' },
+ ]),
verificationStatus: status,
verificationSummary:
status === 'VERIFICATION_SUCCESS' || status === 'VERIFICATION_FAILED' ? faker.lorem.paragraph() : null,
diff --git a/web/src/components/CommentItem.astro b/web/src/components/CommentItem.astro
index 9ae825a..25c7c47 100644
--- a/web/src/components/CommentItem.astro
+++ b/web/src/components/CommentItem.astro
@@ -150,7 +150,7 @@ const commentUrl = makeCommentUrl({ serviceSlug, commentId: comment.id, origin:
checked={comment.suspicious}
/>
-
-
-
-
+
+
+
+
+
+
+
+
{/* Edit Event Form - Hidden by default */}
@@ -673,17 +688,30 @@ if (!service) return Astro.rewrite('/404')
-
-
+
+
+
+
+
+
@@ -844,21 +872,34 @@ if (!service) return Astro.rewrite('/404')
{method.value}
-
-
+
+
+
+
+
+
diff --git a/web/src/pages/admin/services/index.astro b/web/src/pages/admin/services/index.astro
index 2b5a8b4..f5edf34 100644
--- a/web/src/pages/admin/services/index.astro
+++ b/web/src/pages/admin/services/index.astro
@@ -8,7 +8,8 @@ import MyPicture from '../../../components/MyPicture.astro'
import SortArrowIcon from '../../../components/SortArrowIcon.astro'
import Tooltip from '../../../components/Tooltip.astro'
import { getKycLevelInfo } from '../../../constants/kycLevels'
-import { getVerificationStatusInfo } from '../../../constants/verificationStatus'
+import { serviceVisibilities } from '../../../constants/serviceVisibility'
+import { getVerificationStatusInfo, verificationStatuses } from '../../../constants/verificationStatus'
import BaseLayout from '../../../layouts/BaseLayout.astro'
import { cn } from '../../../lib/cn'
import { zodParseQueryParamsStoringErrors } from '../../../lib/parseUrlFilters'
@@ -209,11 +210,11 @@ const truncate = (text: string, length: number) => {
id="visibility"
class="mt-1 w-full rounded-md border border-zinc-700 bg-zinc-900 px-3 py-2 text-sm text-zinc-200 focus:border-blue-500 focus:ring-blue-500 focus:outline-none"
>
-
+
{
- Object.values(ServiceVisibility).map((status) => (
-
))
}
@@ -227,11 +228,11 @@ const truncate = (text: string, length: number) => {
id="verificationStatus"
class="mt-1 w-full rounded-md border border-zinc-700 bg-zinc-900 px-3 py-2 text-sm text-zinc-200 focus:border-blue-500 focus:ring-blue-500 focus:outline-none"
>
-
+
{
- Object.values(VerificationStatus).map((status) => (
-
))
}
diff --git a/web/src/pages/index.astro b/web/src/pages/index.astro
index cedf43b..54c953d 100644
--- a/web/src/pages/index.astro
+++ b/web/src/pages/index.astro
@@ -222,7 +222,9 @@ const where = {
verificationStatus: {
in: includeScams ? uniq([...filters.verification, 'VERIFICATION_FAILED'] as const) : filters.verification,
},
- serviceVisibility: ServiceVisibility.PUBLIC,
+ serviceVisibility: {
+ in: [ServiceVisibility.PUBLIC, ServiceVisibility.ARCHIVED],
+ },
overallScore: { gte: filters['min-score'] },
acceptedCurrencies: filters.currencies.length
? filters['currency-mode'] === 'and'
@@ -372,6 +374,7 @@ const [categories, [services, totalServices], countCommunityOnly, attributes] =
imageUrl: true,
verificationStatus: true,
acceptedCurrencies: true,
+ serviceVisibility: true,
attributes: {
select: {
attribute: {
diff --git a/web/src/pages/service/[slug].astro b/web/src/pages/service/[slug].astro
index ba9d0e7..efed1e4 100644
--- a/web/src/pages/service/[slug].astro
+++ b/web/src/pages/service/[slug].astro
@@ -30,7 +30,7 @@ import { formatContactMethod } from '../../constants/contactMethods'
import { currencies, getCurrencyInfo } from '../../constants/currencies'
import { getEventTypeInfo } from '../../constants/eventTypes'
import { getKycLevelInfo, kycLevels } from '../../constants/kycLevels'
-import { serviceVisibilitiesById } from '../../constants/serviceVisibility'
+import { getServiceVisibilityInfo } from '../../constants/serviceVisibility'
import { getTosHighlightRatingInfo } from '../../constants/tosHighlightRating'
import { getUserSentimentInfo } from '../../constants/userSentiment'
import { getVerificationStatusInfo, verificationStatusesByValue } from '../../constants/verificationStatus'
@@ -240,7 +240,11 @@ const watchingDetails = makeWatchingDetails(dbNotificationPreferences, service?.
if (!service) return Astro.rewrite('/404')
-if (service.serviceVisibility !== 'PUBLIC' && service.serviceVisibility !== 'UNLISTED') {
+if (
+ service.serviceVisibility !== 'PUBLIC' &&
+ service.serviceVisibility !== 'UNLISTED' &&
+ service.serviceVisibility !== 'ARCHIVED'
+) {
return Astro.rewrite('/404')
}
@@ -356,6 +360,8 @@ const ogImageTemplateData = {
score: service.overallScore,
imageUrl: service.imageUrl,
} satisfies OgImageAllTemplatesWithProps
+
+const serviceVisibilityInfo = getServiceVisibilityInfo(service.serviceVisibility)
---
{
- service.serviceVisibility === 'UNLISTED' && (
-
+ (serviceVisibilityInfo.value === 'UNLISTED' || serviceVisibilityInfo.value === 'ARCHIVED') && (
+
- Unlisted service, only accessible via direct link and won't appear in searches.
+ {serviceVisibilityInfo.longDescription}
)
}
+
@@ -1245,7 +1252,8 @@ const ogImageTemplateData = {
{
service.verificationStatus !== 'VERIFICATION_SUCCESS' &&
- service.verificationStatus !== 'VERIFICATION_FAILED' && (
+ service.verificationStatus !== 'VERIFICATION_FAILED' &&
+ service.serviceVisibility !== 'ARCHIVED' && (