162 lines
5.7 KiB
Plaintext
162 lines
5.7 KiB
Plaintext
---
|
|
description:
|
|
globs: web/src/pages,web/src/components
|
|
alwaysApply: false
|
|
---
|
|
- On .astro files, don't forget to include the three dashes (`---`) at the begining of the file and where the server js ends. I noticed that sometimes you forget them.
|
|
- For icons use the `Icon` component from `astro-icon/components`.
|
|
- For icons use the Remix Icon library preferably.
|
|
- Use the `MyPicture` component from `src/components/MyPicture.astro` for images.
|
|
- When redirecting to login use the `makeLoginUrl` function from [redirectUrls.ts](mdc:web/src/lib/redirectUrls.ts) and if the link is for an `<a>` tag, use the `data-astro-reload` attribute. Similar for the logout and impersonate.
|
|
- Don't use the `web/src/pages/admin` pages as example unless explicitly stated or you're creating/editing an admin page.
|
|
- Checkout the @errorBanners.ts @middleware.ts @env.d.ts to see the avilable Astro.locals values.
|
|
- Avoid duplicating similar html code. You can use jsx for loops, create variables in the constants folder, or create separate components.
|
|
- When redirecting to the 404 not found page, use `Astro.rewrite` (Like this example: `if (!user) return Astro.rewrite('/404')`)
|
|
- Include schema markup in the pages when it makes sense. Examples: [[slug].astro](mdc:web/src/pages/service/[slug].astro)
|
|
- When creating forms, we already have utilities, components and established design patterns. Follow this example. (Note that this example may come slightly outdaded, but the overall philosophy doesn't change)
|
|
```astro
|
|
---
|
|
import { actions, isInputError } from 'astro:actions'
|
|
import { z } from 'astro:content'
|
|
|
|
import Captcha from '../../components/Captcha.astro'
|
|
import InputCardGroup from '../../components/InputCardGroup.astro'
|
|
import InputCheckboxGroup from '../../components/InputCheckboxGroup.astro'
|
|
import InputHoneypotTrap from '../../components/InputHoneypotTrap.astro'
|
|
import InputImageFile from '../../components/InputImageFile.astro'
|
|
import InputSubmitButton from '../../components/InputSubmitButton.astro'
|
|
import InputText from '../../components/InputText.astro'
|
|
import InputTextArea from '../../components/InputTextArea.astro'
|
|
import { kycLevels } from '../../constants/kycLevels'
|
|
import BaseLayout from '../../layouts/BaseLayout.astro'
|
|
import { zodParseQueryParamsStoringErrors } from '../../lib/parseUrlFilters'
|
|
import { prisma } from '../../lib/prisma'
|
|
import { makeLoginUrl } from '../../lib/redirectUrls'
|
|
|
|
const user = Astro.locals.user
|
|
if (!user) {
|
|
return Astro.redirect(makeLoginUrl(Astro.url, { message: 'Login to suggest a new service' }))
|
|
}
|
|
|
|
const result = Astro.getActionResult(actions.serviceSuggestion.editService)
|
|
if (result && !result.error) {
|
|
return Astro.redirect(`/service-suggestion/${result.data.serviceSuggestion.id}`)
|
|
}
|
|
const inputErrors = isInputError(result?.error) ? result.error.fields : {}
|
|
|
|
const { data: params } = zodParseQueryParamsStoringErrors(
|
|
{
|
|
serviceId: z.coerce.number().int().positive(),
|
|
notes: z.string().default(''),
|
|
},
|
|
Astro
|
|
)
|
|
|
|
if (!params.serviceId) return Astro.rewrite('/404')
|
|
|
|
const service = await Astro.locals.banners.try(
|
|
'Failed to fetch service',
|
|
async () =>
|
|
prisma.service.findUnique({
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
slug: true,
|
|
description: true,
|
|
overallScore: true,
|
|
kycLevel: true,
|
|
imageUrl: true,
|
|
verificationStatus: true,
|
|
acceptedCurrencies: true,
|
|
categories: {
|
|
select: {
|
|
name: true,
|
|
icon: true,
|
|
},
|
|
},
|
|
},
|
|
where: { id: params.serviceId },
|
|
}),
|
|
null
|
|
)
|
|
|
|
if (!service) return Astro.rewrite('/404')
|
|
---
|
|
|
|
<BaseLayout
|
|
pageTitle="Edit service"
|
|
description="Suggest an edit to service"
|
|
ogImage={{
|
|
template: 'generic',
|
|
title: 'Edit service',
|
|
description: 'Suggest an edit to service',
|
|
icon: 'ri:edit-line',
|
|
}}
|
|
widthClassName="max-w-screen-md"
|
|
>
|
|
<h1 class="font-title mt-12 mb-6 text-center text-3xl font-bold">Edit service</h1>
|
|
|
|
<form method="POST" action={actions.serviceSuggestion.editService} class="space-y-6">
|
|
<input type="hidden" name="serviceId" value={params.serviceId} />
|
|
|
|
<InputText
|
|
label="Service name"
|
|
name="name"
|
|
value={service.name}
|
|
error={inputErrors.name}
|
|
inputProps={{ 'data-custom-value': true, required: true }}
|
|
/>
|
|
|
|
<InputCardGroup
|
|
name="kycLevel"
|
|
label="KYC Level"
|
|
options={kycLevels.map((kycLevel) => ({
|
|
label: kycLevel.name,
|
|
value: kycLevel.id.toString(),
|
|
icon: kycLevel.icon,
|
|
description: `${kycLevel.description}\n\n_KYC Level ${kycLevel.value}/5_`,
|
|
}))}
|
|
iconSize="md"
|
|
cardSize="md"
|
|
required
|
|
error={inputErrors.kycLevel}
|
|
/>
|
|
|
|
<InputCheckboxGroup
|
|
name="categories"
|
|
label="Categories"
|
|
required
|
|
options={categories.map((category) => ({
|
|
label: category.name,
|
|
value: category.id.toString(),
|
|
icon: category.icon,
|
|
}))}
|
|
error={inputErrors.categories}
|
|
/>
|
|
|
|
<InputImageFile
|
|
label="Service Image"
|
|
name="imageFile"
|
|
description="Square image. At least 192x192px. Transparency supported."
|
|
error={inputErrors.imageFile}
|
|
square
|
|
required
|
|
/>
|
|
|
|
<InputTextArea
|
|
label="Note for Moderators"
|
|
name="notes"
|
|
value={params.notes}
|
|
inputProps={{ rows: 10 }}
|
|
error={inputErrors.notes}
|
|
/>
|
|
|
|
<Captcha action={actions.serviceSuggestion.createService} />
|
|
|
|
<InputHoneypotTrap name="message" />
|
|
|
|
<InputSubmitButton hideCancel />
|
|
</form>
|
|
</BaseLayout>
|
|
```
|