Compare commits
4 Commits
release-20
...
release-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85605de8aa | ||
|
|
7a22629c55 | ||
|
|
8deb9acb93 | ||
|
|
61a5448ff5 |
@@ -859,6 +859,22 @@ const generateFakeServiceContactMethod = (serviceId: number) => {
|
|||||||
{
|
{
|
||||||
value: `https://x.com/${faker.internet.username()}`,
|
value: `https://x.com/${faker.internet.username()}`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: `https://matrix.to/#/@${faker.internet.username()}:${faker.internet.domainName()}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: `https://instagram.com/${faker.internet.username()}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: `https://linkedin.com/in/${faker.helpers.slugify(faker.person.fullName())}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: faker.lorem.word({ length: 2 }),
|
||||||
|
value: `https://bitcointalk.org/index.php?topic=${faker.number.int({ min: 1, max: 1000000 }).toString()}.0`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: `https://bitcointalk.org/index.php?topic=${faker.number.int({ min: 1, max: 1000000 }).toString()}.0`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
value: faker.internet.url(),
|
value: faker.internet.url(),
|
||||||
},
|
},
|
||||||
@@ -867,7 +883,7 @@ const generateFakeServiceContactMethod = (serviceId: number) => {
|
|||||||
value: faker.internet.url(),
|
value: faker.internet.url(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: `https://www.linkedin.com/company/${faker.helpers.slugify(faker.company.name())}`,
|
value: `https://linkedin.com/company/${faker.helpers.slugify(faker.company.name())}`,
|
||||||
},
|
},
|
||||||
] as const satisfies Partial<Prisma.ServiceContactMethodCreateInput>[]
|
] as const satisfies Partial<Prisma.ServiceContactMethodCreateInput>[]
|
||||||
|
|
||||||
|
|||||||
@@ -187,13 +187,17 @@ export const adminServiceActions = {
|
|||||||
accept: 'form',
|
accept: 'form',
|
||||||
permissions: 'admin',
|
permissions: 'admin',
|
||||||
input: z.object({
|
input: z.object({
|
||||||
label: z.string().min(1).max(50).optional(),
|
label: z.string().min(1).max(50).nullable(),
|
||||||
value: z.string().url(),
|
value: z.string().url(),
|
||||||
serviceId: z.number().int().positive(),
|
serviceId: z.number().int().positive(),
|
||||||
}),
|
}),
|
||||||
handler: async (input) => {
|
handler: async (input) => {
|
||||||
const contactMethod = await prisma.serviceContactMethod.create({
|
const contactMethod = await prisma.serviceContactMethod.create({
|
||||||
data: input,
|
data: {
|
||||||
|
label: input.label,
|
||||||
|
value: input.value,
|
||||||
|
serviceId: input.serviceId,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
return { contactMethod }
|
return { contactMethod }
|
||||||
},
|
},
|
||||||
@@ -203,16 +207,19 @@ export const adminServiceActions = {
|
|||||||
accept: 'form',
|
accept: 'form',
|
||||||
permissions: 'admin',
|
permissions: 'admin',
|
||||||
input: z.object({
|
input: z.object({
|
||||||
id: z.number().int().positive().optional(),
|
id: z.number().int().positive(),
|
||||||
label: z.string().min(1).max(50).optional(),
|
label: z.string().min(1).max(50).nullable(),
|
||||||
value: z.string().url(),
|
value: z.string().url(),
|
||||||
serviceId: z.number().int().positive(),
|
serviceId: z.number().int().positive(),
|
||||||
}),
|
}),
|
||||||
handler: async (input) => {
|
handler: async (input) => {
|
||||||
const { id, ...data } = input
|
|
||||||
const contactMethod = await prisma.serviceContactMethod.update({
|
const contactMethod = await prisma.serviceContactMethod.update({
|
||||||
where: { id },
|
where: { id: input.id },
|
||||||
data,
|
data: {
|
||||||
|
label: input.label,
|
||||||
|
value: input.value,
|
||||||
|
serviceId: input.serviceId,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
return { contactMethod }
|
return { contactMethod }
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -30,14 +30,14 @@ export const {
|
|||||||
{
|
{
|
||||||
type: 'email',
|
type: 'email',
|
||||||
label: 'Email',
|
label: 'Email',
|
||||||
matcher: /mailto:(.*)/,
|
matcher: /mailto:(.+)/,
|
||||||
formatter: (value) => value,
|
formatter: (value) => value,
|
||||||
icon: 'ri:mail-line',
|
icon: 'ri:mail-line',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'telephone',
|
type: 'telephone',
|
||||||
label: 'Telephone',
|
label: 'Telephone',
|
||||||
matcher: /tel:(.*)/,
|
matcher: /tel:(.+)/,
|
||||||
formatter: (value) => {
|
formatter: (value) => {
|
||||||
return parsePhoneNumberWithError(value).formatInternational()
|
return parsePhoneNumberWithError(value).formatInternational()
|
||||||
},
|
},
|
||||||
@@ -46,7 +46,7 @@ export const {
|
|||||||
{
|
{
|
||||||
type: 'whatsapp',
|
type: 'whatsapp',
|
||||||
label: 'WhatsApp',
|
label: 'WhatsApp',
|
||||||
matcher: /https?:\/\/(?:www\.)?wa\.me\/(.*)\/?/,
|
matcher: /https?:\/\/(?:www\.)?wa\.me\/(.+)/,
|
||||||
formatter: (value) => {
|
formatter: (value) => {
|
||||||
return parsePhoneNumberWithError(value).formatInternational()
|
return parsePhoneNumberWithError(value).formatInternational()
|
||||||
},
|
},
|
||||||
@@ -55,42 +55,35 @@ export const {
|
|||||||
{
|
{
|
||||||
type: 'telegram',
|
type: 'telegram',
|
||||||
label: 'Telegram',
|
label: 'Telegram',
|
||||||
matcher: /https?:\/\/(?:www\.)?t\.me\/(.*)\/?/,
|
matcher: /https?:\/\/(?:www\.)?t\.me\/(.+)/,
|
||||||
formatter: (value) => `t.me/${value}`,
|
formatter: (value) => `t.me/${value}`,
|
||||||
icon: 'ri:telegram-line',
|
icon: 'ri:telegram-line',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'linkedin',
|
type: 'linkedin',
|
||||||
label: 'LinkedIn',
|
label: 'LinkedIn',
|
||||||
matcher: /https?:\/\/(?:www\.)?linkedin\.com\/(?:in|company)\/(.*)\/?/,
|
matcher: /https?:\/\/(?:www\.)?linkedin\.com\/(?:in|company)\/(.+)/,
|
||||||
formatter: (value) => `in/${value}`,
|
formatter: (value) => `in/${value}`,
|
||||||
icon: 'ri:linkedin-box-line',
|
icon: 'ri:linkedin-box-line',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
type: 'website',
|
|
||||||
label: 'Website',
|
|
||||||
matcher: /https?:\/\/(?:www\.)?((?:[a-zA-Z0-9-]+\.)+[a-zA-Z]+)/,
|
|
||||||
formatter: (value) => value,
|
|
||||||
icon: 'ri:global-line',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: 'x',
|
type: 'x',
|
||||||
label: 'X',
|
label: 'X',
|
||||||
matcher: /https?:\/\/(?:www\.)?x\.com\/(.*)\/?/,
|
matcher: /https?:\/\/(?:www\.)?x\.com\/(.+)/,
|
||||||
formatter: (value) => `@${value}`,
|
formatter: (value) => `@${value}`,
|
||||||
icon: 'ri:twitter-x-line',
|
icon: 'ri:twitter-x-line',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'instagram',
|
type: 'instagram',
|
||||||
label: 'Instagram',
|
label: 'Instagram',
|
||||||
matcher: /https?:\/\/(?:www\.)?instagram\.com\/(.*)\/?/,
|
matcher: /https?:\/\/(?:www\.)?instagram\.com\/(.+)/,
|
||||||
formatter: (value) => `@${value}`,
|
formatter: (value) => `@${value}`,
|
||||||
icon: 'ri:instagram-line',
|
icon: 'ri:instagram-line',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'matrix',
|
type: 'matrix',
|
||||||
label: 'Matrix',
|
label: 'Matrix',
|
||||||
matcher: /https?:\/\/(?:www\.)?matrix\.to\/#\/(.*)\/?/,
|
matcher: /https?:\/\/(?:www\.)?matrix\.to\/#\/(.+)/,
|
||||||
formatter: (value) => value,
|
formatter: (value) => value,
|
||||||
icon: 'ri:hashtag',
|
icon: 'ri:hashtag',
|
||||||
},
|
},
|
||||||
@@ -101,6 +94,14 @@ export const {
|
|||||||
formatter: () => 'BitcoinTalk',
|
formatter: () => 'BitcoinTalk',
|
||||||
icon: 'ri:btc-line',
|
icon: 'ri:btc-line',
|
||||||
},
|
},
|
||||||
|
// Website must go last because it's a catch-all
|
||||||
|
{
|
||||||
|
type: 'website',
|
||||||
|
label: 'Website',
|
||||||
|
matcher: /https?:\/\/(?:www\.)?((?:[a-zA-Z0-9-]+\.)+[a-zA-Z]+)/,
|
||||||
|
formatter: (value) => value,
|
||||||
|
icon: 'ri:global-line',
|
||||||
|
},
|
||||||
] as const satisfies ContactMethodInfo[]
|
] as const satisfies ContactMethodInfo[]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -170,8 +170,6 @@ const errorTextClasses = 'mt-1 text-xs text-red-400'
|
|||||||
</h1>
|
</h1>
|
||||||
<a
|
<a
|
||||||
href={`/service/${service.slug}`}
|
href={`/service/${service.slug}`}
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
class="inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-sm text-sky-400 hover:text-sky-300 hover:underline focus:ring-2 focus:ring-sky-500 focus:ring-offset-2 focus:ring-offset-zinc-900 focus:outline-none"
|
class="inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-sm text-sky-400 hover:text-sky-300 hover:underline focus:ring-2 focus:ring-sky-500 focus:ring-offset-2 focus:ring-offset-zinc-900 focus:outline-none"
|
||||||
>
|
>
|
||||||
<Icon name="ri:external-link-line" class="size-4" />
|
<Icon name="ri:external-link-line" class="size-4" />
|
||||||
@@ -405,8 +403,7 @@ const errorTextClasses = 'mt-1 text-xs text-red-400'
|
|||||||
}))}
|
}))}
|
||||||
selectedValue={service.serviceVisibility}
|
selectedValue={service.serviceVisibility}
|
||||||
error={serviceInputErrors.serviceVisibility}
|
error={serviceInputErrors.serviceVisibility}
|
||||||
cardSize="md"
|
cardSize="sm"
|
||||||
iconSize="md"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1018,12 +1015,10 @@ const errorTextClasses = 'mt-1 text-xs text-red-400'
|
|||||||
<div class="flex-grow space-y-1">
|
<div class="flex-grow space-y-1">
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<Icon name={contactMethodInfo.icon} class="size-4 text-zinc-300" />
|
<Icon name={contactMethodInfo.icon} class="size-4 text-zinc-300" />
|
||||||
<span class="text-md font-semibold text-zinc-100">
|
<span class="text-md font-semibold text-zinc-100">{contactMethodInfo.label}</span>
|
||||||
{method.label ?? contactMethodInfo.label}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<p class="text-sm text-pretty text-zinc-400">
|
<p class="text-sm text-pretty text-zinc-400">
|
||||||
{contactMethodInfo.formattedValue}{' '}
|
{method.label ?? contactMethodInfo.formattedValue}{' '}
|
||||||
<span class="text-zinc-500">({method.value})</span>
|
<span class="text-zinc-500">({method.value})</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -1068,7 +1063,7 @@ const errorTextClasses = 'mt-1 text-xs text-red-400'
|
|||||||
id={`contactLabel-${method.id}`}
|
id={`contactLabel-${method.id}`}
|
||||||
value={method.label}
|
value={method.label}
|
||||||
class={inputBaseClasses}
|
class={inputBaseClasses}
|
||||||
placeholder={contactMethodInfo.label}
|
placeholder={contactMethodInfo.formattedValue}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
Reference in New Issue
Block a user