Compare commits
4 Commits
release-95
...
release-99
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5d1fb9a5d | ||
|
|
28b84a7d9b | ||
|
|
7a294cb0a1 | ||
|
|
349c26a4df |
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Service" ADD COLUMN "strictCommentingEnabled" BOOLEAN NOT NULL DEFAULT false;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Service" ADD COLUMN "commentSectionMessage" TEXT;
|
||||
@@ -406,6 +406,9 @@ model Service {
|
||||
Notification Notification[]
|
||||
affiliatedUsers ServiceUser[] @relation("ServiceUsers")
|
||||
|
||||
strictCommentingEnabled Boolean @default(false)
|
||||
commentSectionMessage String?
|
||||
|
||||
@@index([listedAt])
|
||||
@@index([approvedAt])
|
||||
@@index([verifiedAt])
|
||||
|
||||
@@ -720,6 +720,8 @@ const generateFakeService = (users: User[]) => {
|
||||
}),
|
||||
{ probability: 0.33 }
|
||||
),
|
||||
strictCommentingEnabled: faker.datatype.boolean(0.33333),
|
||||
commentSectionMessage: faker.helpers.maybe(() => faker.lorem.paragraph(), { probability: 0.3 }),
|
||||
} as const satisfies Prisma.ServiceCreateInput
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,8 @@ const serviceSchemaBase = z.object({
|
||||
overallScore: zodCohercedNumber(z.number().int().min(0).max(10)).optional(),
|
||||
serviceVisibility: z.nativeEnum(ServiceVisibility),
|
||||
internalNote: z.string().optional(),
|
||||
strictCommentingEnabled: z.boolean().optional().default(false),
|
||||
commentSectionMessage: z.string().trim().min(3).max(1000).optional().nullable().default(null),
|
||||
})
|
||||
|
||||
// Define schema for the create action input
|
||||
@@ -127,6 +129,8 @@ export const adminServiceActions = {
|
||||
verificationSummary: input.verificationSummary,
|
||||
verificationProofMd: input.verificationProofMd,
|
||||
acceptedCurrencies: input.acceptedCurrencies,
|
||||
strictCommentingEnabled: input.strictCommentingEnabled,
|
||||
commentSectionMessage: input.commentSectionMessage,
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||
referral: input.referral || null,
|
||||
serviceVisibility: input.serviceVisibility,
|
||||
@@ -247,6 +251,8 @@ export const adminServiceActions = {
|
||||
verificationSummary: input.verificationSummary,
|
||||
verificationProofMd: input.verificationProofMd,
|
||||
acceptedCurrencies: input.acceptedCurrencies,
|
||||
strictCommentingEnabled: input.strictCommentingEnabled,
|
||||
commentSectionMessage: input.commentSectionMessage,
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||
referral: input.referral || null,
|
||||
serviceVisibility: input.serviceVisibility,
|
||||
|
||||
@@ -17,6 +17,7 @@ import type { CommentStatus, Prisma } from '@prisma/client'
|
||||
const COMMENT_RATE_LIMIT_WINDOW_MINUTES = 2
|
||||
const MAX_COMMENTS_PER_WINDOW = 1
|
||||
const MAX_COMMENTS_PER_WINDOW_VERIFIED_USER = 10
|
||||
export const COMMENT_ORDER_ID_MAX_LENGTH = 600
|
||||
|
||||
export const commentActions = {
|
||||
vote: defineProtectedAction({
|
||||
@@ -103,7 +104,7 @@ export const commentActions = {
|
||||
issueFundsBlocked: z.coerce.boolean().optional(),
|
||||
issueScam: z.coerce.boolean().optional(),
|
||||
issueDetails: z.string().max(120).optional(),
|
||||
orderId: z.string().max(100).optional(),
|
||||
orderId: z.string().max(COMMENT_ORDER_ID_MAX_LENGTH).optional(),
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
if (data.rating && data.parentId) {
|
||||
|
||||
@@ -85,7 +85,7 @@ const Tag = announcement.link ? 'a' : 'div'
|
||||
</div>
|
||||
|
||||
{
|
||||
!!announcement.linkText && (
|
||||
!!announcement.link && !!announcement.linkText && (
|
||||
<div class="text-day-300 group-focus-visible:outline-primary transition-background 2xs:px-4 relative inline-flex h-full shrink-0 cursor-pointer items-center justify-center gap-1.5 overflow-hidden rounded-full border border-white/20 bg-black/10 p-[1px] px-1 py-1 text-sm font-medium shadow-sm backdrop-blur-3xl transition-colors group-hover:bg-white/5 group-focus-visible:ring-2 group-focus-visible:ring-blue-500 group-focus-visible:ring-offset-2 group-focus-visible:ring-offset-black/80 sm:min-w-[120px]">
|
||||
<span class="2xs:inline-block hidden">{announcement.linkText}</span>
|
||||
<Icon
|
||||
|
||||
@@ -33,6 +33,7 @@ type Props = HTMLAttributes<'div'> & {
|
||||
highlightedCommentId: number | null
|
||||
serviceSlug: string
|
||||
itemReviewedId: string
|
||||
strictCommentingEnabled?: boolean
|
||||
}
|
||||
|
||||
const {
|
||||
@@ -42,6 +43,7 @@ const {
|
||||
highlightedCommentId = null,
|
||||
serviceSlug,
|
||||
itemReviewedId,
|
||||
strictCommentingEnabled,
|
||||
class: className,
|
||||
...htmlProps
|
||||
} = Astro.props
|
||||
@@ -492,6 +494,7 @@ const commentUrl = makeCommentUrl({ serviceSlug, commentId: comment.id, origin:
|
||||
serviceId={comment.serviceId}
|
||||
parentId={comment.id}
|
||||
commentId={comment.id}
|
||||
strictCommentingEnabled={strictCommentingEnabled}
|
||||
class="mt-2 hidden peer-checked/collapse:hidden peer-checked/reply:block"
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components'
|
||||
import { Markdown } from 'astro-remote'
|
||||
import { actions } from 'astro:actions'
|
||||
|
||||
import { COMMENT_ORDER_ID_MAX_LENGTH } from '../actions/comment'
|
||||
import { cn } from '../lib/cn'
|
||||
import { makeLoginUrl } from '../lib/redirectUrls'
|
||||
|
||||
@@ -20,6 +22,8 @@ type Props = Omit<HTMLAttributes<'form'>, 'action' | 'enctype' | 'method'> & {
|
||||
serviceId: number
|
||||
parentId?: number
|
||||
commentId?: number
|
||||
strictCommentingEnabled?: boolean
|
||||
commentSectionMessage?: string | null
|
||||
activeRatingComment?: Prisma.CommentGetPayload<{
|
||||
select: {
|
||||
id: true
|
||||
@@ -28,7 +32,16 @@ type Props = Omit<HTMLAttributes<'form'>, 'action' | 'enctype' | 'method'> & {
|
||||
}> | null
|
||||
}
|
||||
|
||||
const { serviceId, parentId, commentId, activeRatingComment, class: className, ...htmlProps } = Astro.props
|
||||
const {
|
||||
serviceId,
|
||||
parentId,
|
||||
commentId,
|
||||
activeRatingComment,
|
||||
strictCommentingEnabled,
|
||||
commentSectionMessage,
|
||||
class: className,
|
||||
...htmlProps
|
||||
} = Astro.props
|
||||
|
||||
const MIN_COMMENT_LENGTH = parentId ? 10 : 30
|
||||
|
||||
@@ -88,69 +101,83 @@ const userCommentsDisabled = user ? user.karmaUnlocks.commentsDisabled : false
|
||||
</div>
|
||||
|
||||
{!parentId ? (
|
||||
<div class="[&:has(input[name='rating'][value='']:checked)_[data-show-if-rating]]:hidden">
|
||||
<div class="flex flex-wrap gap-4">
|
||||
<InputRating name="rating" label="Rating" />
|
||||
<>
|
||||
<div class="[&:has(input[name='rating'][value='']:checked)_[data-show-if-rating]]:hidden">
|
||||
<div class="flex flex-wrap gap-4">
|
||||
<InputRating name="rating" label="Rating" />
|
||||
|
||||
<InputWrapper label="I experienced..." name="tags">
|
||||
<label class="flex cursor-pointer items-center gap-2">
|
||||
<input type="checkbox" name="issueKycRequested" class="text-red-400" />
|
||||
<span class="flex items-center gap-1 text-xs text-red-400">
|
||||
<Icon name="ri:user-forbid-fill" class="size-3" />
|
||||
KYC Issue
|
||||
</span>
|
||||
</label>
|
||||
<InputWrapper label="I experienced..." name="tags">
|
||||
<label class="flex cursor-pointer items-center gap-2">
|
||||
<input type="checkbox" name="issueKycRequested" class="text-red-400" />
|
||||
<span class="flex items-center gap-1 text-xs text-red-400">
|
||||
<Icon name="ri:user-forbid-fill" class="size-3" />
|
||||
KYC Issue
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label class="flex cursor-pointer items-center gap-2">
|
||||
<input type="checkbox" name="issueFundsBlocked" class="text-orange-400" />
|
||||
<span class="flex items-center gap-1 text-xs text-orange-400">
|
||||
<Icon name="ri:wallet-3-fill" class="size-3" />
|
||||
Funds Blocked
|
||||
</span>
|
||||
</label>
|
||||
</InputWrapper>
|
||||
<label class="flex cursor-pointer items-center gap-2">
|
||||
<input type="checkbox" name="issueFundsBlocked" class="text-orange-400" />
|
||||
<span class="flex items-center gap-1 text-xs text-orange-400">
|
||||
<Icon name="ri:wallet-3-fill" class="size-3" />
|
||||
Funds Blocked
|
||||
</span>
|
||||
</label>
|
||||
</InputWrapper>
|
||||
|
||||
<InputText
|
||||
label="Order ID"
|
||||
name="orderId"
|
||||
inputProps={{
|
||||
maxlength: 100,
|
||||
placeholder: 'Order ID / URL / Proof',
|
||||
class: 'bg-night-800',
|
||||
}}
|
||||
descriptionLabel="Only visible to admins, to verify your comment"
|
||||
class="grow"
|
||||
/>
|
||||
</div>
|
||||
<InputText
|
||||
label="Order ID"
|
||||
name="orderId"
|
||||
inputProps={{
|
||||
maxlength: COMMENT_ORDER_ID_MAX_LENGTH,
|
||||
placeholder: 'Order ID / URL / Proof',
|
||||
class: 'bg-night-800',
|
||||
required: strictCommentingEnabled,
|
||||
}}
|
||||
descriptionLabel="Only visible to admins, to verify your comment"
|
||||
class="grow"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 flex items-start justify-end gap-2">
|
||||
{!!activeRatingComment?.rating && (
|
||||
<div
|
||||
class="rounded-sm bg-yellow-500/10 px-2 py-1.5 text-xs text-yellow-400"
|
||||
data-show-if-rating
|
||||
>
|
||||
<Icon name="ri:information-line" class="mr-1 inline size-3.5" />
|
||||
<a
|
||||
href={`${Astro.url.origin}${Astro.url.pathname}#comment-${activeRatingComment.id}`}
|
||||
class="inline-flex items-center gap-1 underline"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
<div class="mt-4 flex flex-wrap items-start justify-end gap-x-4 gap-y-2">
|
||||
{!!activeRatingComment?.rating && (
|
||||
<div
|
||||
class="mt-1 rounded-sm bg-yellow-500/10 px-2 py-1.5 text-xs text-yellow-400"
|
||||
data-show-if-rating
|
||||
>
|
||||
Your previous rating
|
||||
<Icon name="ri:external-link-line" class="inline size-2.5 align-[-0.1em]" />
|
||||
</a>
|
||||
of
|
||||
{[
|
||||
activeRatingComment.rating.toLocaleString(),
|
||||
<Icon name="ri:star-fill" class="inline size-3 align-[-0.1em]" />,
|
||||
]}
|
||||
won't count for the total.
|
||||
</div>
|
||||
)}
|
||||
<Icon name="ri:information-line" class="mr-1 inline size-3.5" />
|
||||
<a
|
||||
href={`${Astro.url.origin}${Astro.url.pathname}#comment-${activeRatingComment.id}`}
|
||||
class="inline-flex items-center gap-1 underline"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Your previous rating
|
||||
<Icon name="ri:external-link-line" class="inline size-2.5 align-[-0.1em]" />
|
||||
</a>
|
||||
of
|
||||
{[
|
||||
activeRatingComment.rating.toLocaleString(),
|
||||
<Icon name="ri:star-fill" class="inline size-3 align-[-0.1em]" />,
|
||||
]}
|
||||
won't count for the total.
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Button type="submit" label="Send" icon="ri:send-plane-2-line" />
|
||||
<div class="flex flex-wrap items-start justify-end gap-x-4 gap-y-2">
|
||||
{!!commentSectionMessage && (
|
||||
<div class="flex items-start gap-1 pt-1.5">
|
||||
<Icon name="ri:information-line" class="mt-1.25 inline size-3.5" />
|
||||
<div class="prose prose-invert prose-sm text-day-200 max-w-none grow">
|
||||
<Markdown content={commentSectionMessage} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Button type="submit" label="Send" icon="ri:send-plane-2-line" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<Button type="submit" label="Reply" icon="ri:reply-line" />
|
||||
|
||||
@@ -35,6 +35,8 @@ type Props = {
|
||||
name: true
|
||||
description: true
|
||||
createdAt: true
|
||||
strictCommentingEnabled: true
|
||||
commentSectionMessage: true
|
||||
}
|
||||
}>
|
||||
}
|
||||
@@ -173,7 +175,13 @@ function makeReplySchema(comment: CommentWithRepliesPopulated): Comment {
|
||||
comment: comments.map(makeReplySchema),
|
||||
} as WithContext<DiscussionForumPosting>}
|
||||
/>
|
||||
<CommentReply serviceId={service.id} activeRatingComment={activeRatingComment} class="xs:mb-4 mb-2" />
|
||||
<CommentReply
|
||||
serviceId={service.id}
|
||||
activeRatingComment={activeRatingComment}
|
||||
strictCommentingEnabled={service.strictCommentingEnabled}
|
||||
commentSectionMessage={service.commentSectionMessage}
|
||||
class="xs:mb-4 mb-2"
|
||||
/>
|
||||
|
||||
<div class="mb-6 flex flex-wrap items-center justify-between gap-2">
|
||||
<div class="flex items-center">
|
||||
@@ -258,6 +266,7 @@ function makeReplySchema(comment: CommentWithRepliesPopulated): Comment {
|
||||
showPending={params.showPending}
|
||||
serviceSlug={service.slug}
|
||||
itemReviewedId={itemReviewedId}
|
||||
strictCommentingEnabled={service.strictCommentingEnabled}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
|
||||
@@ -7,6 +7,8 @@ import type { ComponentProps } from 'astro/types'
|
||||
|
||||
type Props = Pick<ComponentProps<typeof InputWrapper>, 'error' | 'name' | 'required'> & {
|
||||
disabled?: boolean
|
||||
checked?: boolean
|
||||
descriptionInline?: string
|
||||
id?: string
|
||||
} & (
|
||||
| {
|
||||
@@ -19,13 +21,11 @@ type Props = Pick<ComponentProps<typeof InputWrapper>, 'error' | 'name' | 'requi
|
||||
}
|
||||
)
|
||||
|
||||
const { disabled, name, required, error, id, label } = Astro.props
|
||||
const { disabled, name, required, error, id, label, checked, descriptionInline } = Astro.props
|
||||
|
||||
const hasError = !!error && error.length > 0
|
||||
---
|
||||
|
||||
{}
|
||||
|
||||
<div>
|
||||
<label
|
||||
class={cn(
|
||||
@@ -41,9 +41,11 @@ const hasError = !!error && error.length > 0
|
||||
name={name}
|
||||
required={required}
|
||||
disabled={disabled}
|
||||
checked={checked}
|
||||
class={cn(disabled && 'opacity-50')}
|
||||
/>
|
||||
<span class="text-sm leading-none text-pretty">{label ?? <slot />}</span>
|
||||
{descriptionInline && <p class="text-day-400 text-xs">{descriptionInline}</p>}
|
||||
</label>
|
||||
|
||||
{
|
||||
|
||||
@@ -414,7 +414,10 @@ const {
|
||||
class={cn('mr-2 size-3 shrink-0 opacity-80', attribute.typeInfo.classNames.icon)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
<span
|
||||
class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap"
|
||||
title={attribute.title}
|
||||
>
|
||||
{attribute.title}
|
||||
</span>
|
||||
<span class="text-day-500 ml-2 font-normal">{attribute._count.services}</span>
|
||||
@@ -429,7 +432,10 @@ const {
|
||||
class={cn('mr-2 size-3 shrink-0 opacity-100', attribute.typeInfo.classNames.icon)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
<span
|
||||
class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap"
|
||||
title={attribute.title}
|
||||
>
|
||||
{attribute.title}
|
||||
</span>
|
||||
<span class="text-day-500 ml-2 font-normal">{attribute._count.services}</span>
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Icon } from 'astro-icon/components'
|
||||
import { uniq, orderBy } from 'lodash-es'
|
||||
|
||||
import { getCurrencyInfo } from '../constants/currencies'
|
||||
import { getKycLevelInfo } from '../constants/kycLevels'
|
||||
import { verificationStatusesByValue, getVerificationStatusInfo } from '../constants/verificationStatus'
|
||||
import { areEqualArraysWithoutOrder } from '../lib/arrays'
|
||||
import { cn } from '../lib/cn'
|
||||
@@ -160,8 +159,7 @@ const searchTitle = (() => {
|
||||
}
|
||||
|
||||
if (filters['max-kyc'] === 0) {
|
||||
const kycLevelInfo = getKycLevelInfo(String(filters['max-kyc']))
|
||||
kycLevel = `with ${kycLevelInfo.name}`
|
||||
kycLevel = 'without KYC'
|
||||
prefix = ''
|
||||
} else if (filters['max-kyc'] <= 3) {
|
||||
kycLevel = `with KYC level ${filters['max-kyc']} or better`
|
||||
|
||||
@@ -10,6 +10,7 @@ import Button from '../../../../components/Button.astro'
|
||||
import FormSection from '../../../../components/FormSection.astro'
|
||||
import FormSubSection from '../../../../components/FormSubSection.astro'
|
||||
import InputCardGroup from '../../../../components/InputCardGroup.astro'
|
||||
import InputCheckbox from '../../../../components/InputCheckbox.astro'
|
||||
import InputCheckboxGroup from '../../../../components/InputCheckboxGroup.astro'
|
||||
import InputImageFile from '../../../../components/InputImageFile.astro'
|
||||
import InputSelect from '../../../../components/InputSelect.astro'
|
||||
@@ -545,6 +546,24 @@ const apiCalls = await Astro.locals.banners.try(
|
||||
cardSize="sm"
|
||||
/>
|
||||
|
||||
<InputCheckbox
|
||||
label="Strict Commenting"
|
||||
name="strictCommentingEnabled"
|
||||
checked={service.strictCommentingEnabled}
|
||||
descriptionInline="Require proof of being a client for comments."
|
||||
/>
|
||||
|
||||
<InputTextArea
|
||||
label="Comment Section Message"
|
||||
name="commentSectionMessage"
|
||||
value={service.commentSectionMessage ?? ''}
|
||||
description="Markdown supported"
|
||||
inputProps={{
|
||||
rows: 4,
|
||||
}}
|
||||
error={serviceInputErrors.commentSectionMessage}
|
||||
/>
|
||||
|
||||
<InputSubmitButton label="Update" icon="ri:save-line" hideCancel />
|
||||
</form>
|
||||
</FormSection>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { AttributeCategory, Currency, VerificationStatus } from '@prisma/client'
|
||||
import { Icon } from 'astro-icon/components'
|
||||
import { actions, isInputError } from 'astro:actions'
|
||||
|
||||
import InputCheckbox from '../../../components/InputCheckbox.astro'
|
||||
import BaseLayout from '../../../layouts/BaseLayout.astro'
|
||||
import { cn } from '../../../lib/cn'
|
||||
import { prisma } from '../../../lib/prisma'
|
||||
@@ -368,6 +369,35 @@ const inputErrors = isInputError(result?.error) ? result.error.fields : {}
|
||||
}
|
||||
</div>
|
||||
|
||||
<InputCheckbox
|
||||
label="Strict Commenting"
|
||||
name="strictCommentingEnabled"
|
||||
checked={false}
|
||||
descriptionInline="Require proof of being a client for comments."
|
||||
/>
|
||||
|
||||
<div>
|
||||
<label for="commentSectionMessage" class="font-title mb-2 block text-sm text-green-500"
|
||||
>Comment Section Message</label
|
||||
>
|
||||
<div class="space-y-2">
|
||||
<textarea
|
||||
transition:persist
|
||||
name="commentSectionMessage"
|
||||
id="commentSectionMessage"
|
||||
rows={4}
|
||||
placeholder="Markdown supported. This message will be displayed in the comment section for root comments."
|
||||
class="font-title w-full rounded-md border border-green-500/30 bg-black/50 p-2 text-gray-300 placeholder-gray-500 focus:border-green-500 focus:ring-green-500"
|
||||
set:text=""
|
||||
/>
|
||||
</div>
|
||||
{
|
||||
inputErrors.commentSectionMessage && (
|
||||
<p class="font-title mt-1 text-sm text-red-500">{inputErrors.commentSectionMessage.join(', ')}</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="font-title inline-flex justify-center rounded-md border border-green-500/30 bg-green-500/10 px-4 py-2 text-sm text-green-400 shadow-xs transition-colors duration-200 hover:bg-green-500/20 focus:ring-2 focus:ring-green-500 focus:ring-offset-2 focus:ring-offset-black focus:outline-hidden"
|
||||
|
||||
@@ -102,6 +102,8 @@ const [service, dbNotificationPreferences] = await Astro.locals.banners.tryMany(
|
||||
userSentimentAt: true,
|
||||
averageUserRating: true,
|
||||
isRecentlyApproved: true,
|
||||
strictCommentingEnabled: true,
|
||||
commentSectionMessage: true,
|
||||
contactMethods: {
|
||||
select: {
|
||||
value: true,
|
||||
@@ -1533,6 +1535,20 @@ const activeEventToShow =
|
||||
<li>Moderation is light.</li>
|
||||
<li>Double-check before trusting.</li>
|
||||
</ul>
|
||||
|
||||
{
|
||||
service.strictCommentingEnabled && (
|
||||
<p class="mt-2">
|
||||
<Icon
|
||||
name="ri:verified-badge-fill"
|
||||
class="me-0.5 inline-block size-4 align-[-0.3em] text-orange-100/95"
|
||||
/>
|
||||
<span class="font-medium text-orange-100/95">Proof of being a client required</span>, for this
|
||||
service.
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
<div class="absolute inset-y-2 right-2 flex flex-col justify-center">
|
||||
<Icon name="ri:alert-line" class="xs:opacity-20 h-full max-h-16 w-auto opacity-10" />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user