mirror of
https://github.com/supabase/supabase.git
synced 2026-05-08 18:00:20 -04:00
308cd791a2
This PR preps the monorepo for a migration to Tailwind v4: - Bump all Tailwind dependencies and libraries to the latest possible version, while still compatible with Tailwind 3. - Cleans up obsolete Tailwind 3 specific options and configs. - Cleans up unused CSS files and fixes the CSS imports. - Migrates all `important` uses in `@apply` lines to using the `!` prefix. - Move `typography.css` to the `config` package and import it from the apps. - Migrated all occurrences of `flex-grow`, `flex-shrink`, `overflow-clip` and `overflow-ellipsis` since they're deprecated and will be removed in Tailwind 4. - Make the default theme object typesafe in the `ui` package. - Migrate all `bg-opacity`, `border-opacity`, `ring-opacity` and `divider-opacity` to the new format where they're declared as part of the property color. - Bump and unify all imports of `postcss` dependency.
157 lines
5.0 KiB
TypeScript
157 lines
5.0 KiB
TypeScript
import { zodResolver } from '@hookform/resolvers/zod'
|
|
import { useParams } from 'common'
|
|
import dayjs from 'dayjs'
|
|
import { useEffect } from 'react'
|
|
import { useForm } from 'react-hook-form'
|
|
import { toast } from 'sonner'
|
|
import {
|
|
Button,
|
|
cn,
|
|
Form,
|
|
FormControl,
|
|
FormField,
|
|
Input_Shadcn_,
|
|
Modal,
|
|
Select_Shadcn_,
|
|
SelectContent_Shadcn_,
|
|
SelectItem_Shadcn_,
|
|
SelectTrigger_Shadcn_,
|
|
Separator,
|
|
} from 'ui'
|
|
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
|
import * as z from 'zod'
|
|
|
|
import { useUserUpdateMutation } from '@/data/auth/user-update-mutation'
|
|
import { User } from '@/data/auth/users-infinite-query'
|
|
|
|
interface BanUserModalProps {
|
|
visible: boolean
|
|
user: User
|
|
onClose: () => void
|
|
}
|
|
|
|
export const BanUserModal = ({ visible, user, onClose }: BanUserModalProps) => {
|
|
const { ref: projectRef } = useParams()
|
|
|
|
const { mutate: updateUser, isPending: isBanningUser } = useUserUpdateMutation({
|
|
onSuccess: (_, vars) => {
|
|
const bannedUntil = dayjs()
|
|
.add(Number(vars.banDuration), 'hours')
|
|
.format('DD MMM YYYY HH:mm (ZZ)')
|
|
toast.success(`User banned successfully until ${bannedUntil}`)
|
|
onClose()
|
|
},
|
|
})
|
|
|
|
const FormSchema = z.object({
|
|
value: z.string().min(1, { message: 'Please provide a duration' }),
|
|
unit: z.enum(['hours', 'days']),
|
|
})
|
|
type FormType = z.infer<typeof FormSchema>
|
|
const defaultValues: FormType = { value: '24', unit: 'hours' }
|
|
const form = useForm<FormType>({
|
|
mode: 'onBlur',
|
|
reValidateMode: 'onChange',
|
|
resolver: zodResolver(FormSchema),
|
|
defaultValues,
|
|
})
|
|
|
|
const { value, unit } = form.watch()
|
|
const bannedUntil = dayjs().add(Number(value), unit).format('DD MMM YYYY HH:mm (ZZ)')
|
|
|
|
const onSubmit = (data: FormType) => {
|
|
if (projectRef === undefined) return console.error('Project ref is required')
|
|
if (user.id === undefined) {
|
|
return toast.error(`Failed to ban user: User ID not found`)
|
|
}
|
|
|
|
const durationHours = data.unit === 'hours' ? Number(data.value) : Number(data.value) * 24
|
|
|
|
updateUser({
|
|
projectRef,
|
|
userId: user.id,
|
|
banDuration: durationHours,
|
|
})
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (visible) form.reset(defaultValues)
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [visible])
|
|
|
|
return (
|
|
<Modal
|
|
hideFooter
|
|
visible={visible}
|
|
size="small"
|
|
header="Confirm to ban user"
|
|
onCancel={() => onClose()}
|
|
>
|
|
<Form {...form}>
|
|
<form onSubmit={form.handleSubmit(onSubmit)}>
|
|
<Modal.Content className="flex flex-col gap-y-3">
|
|
<p className="text-sm">
|
|
This will revoke the user's access to your project and prevent them from logging in
|
|
for a specified duration.
|
|
</p>
|
|
<div className="flex items-start gap-x-2 [&>div:first-child]:grow">
|
|
<FormField
|
|
control={form.control}
|
|
name="value"
|
|
render={({ field }) => (
|
|
<FormItemLayout className="[&>div>div]:mt-0" label="Set a ban duration">
|
|
<FormControl>
|
|
<Input_Shadcn_ {...field} />
|
|
</FormControl>
|
|
</FormItemLayout>
|
|
)}
|
|
/>
|
|
<FormField
|
|
control={form.control}
|
|
name="unit"
|
|
render={({ field }) => (
|
|
<FormItemLayout className="[&>div>div]:mt-0 mt-[33px]">
|
|
<FormControl>
|
|
<Select_Shadcn_
|
|
{...field}
|
|
value={field.value}
|
|
onValueChange={(value) => form.setValue('unit', value as 'hours' | 'days')}
|
|
>
|
|
<SelectTrigger_Shadcn_ className="capitalize w-24">
|
|
{field.value}
|
|
</SelectTrigger_Shadcn_>
|
|
<SelectContent_Shadcn_>
|
|
<SelectItem_Shadcn_ value="hours">Hours</SelectItem_Shadcn_>
|
|
<SelectItem_Shadcn_ value="days">Days</SelectItem_Shadcn_>
|
|
</SelectContent_Shadcn_>
|
|
</Select_Shadcn_>
|
|
</FormControl>
|
|
</FormItemLayout>
|
|
)}
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<p className="text-sm text-foreground-lighter">
|
|
This user will not be able to log in until:
|
|
</p>
|
|
<p className={cn('text-sm', !value && 'text-foreground-light')}>
|
|
{!!value ? bannedUntil : 'Invalid duration set'}
|
|
</p>
|
|
</div>
|
|
</Modal.Content>
|
|
<Separator />
|
|
<Modal.Content className="flex justify-end gap-2">
|
|
<Button type="default" disabled={isBanningUser} onClick={() => onClose()}>
|
|
Cancel
|
|
</Button>
|
|
<Button type="warning" htmlType="submit" loading={isBanningUser}>
|
|
Confirm ban
|
|
</Button>
|
|
</Modal.Content>
|
|
</form>
|
|
</Form>
|
|
</Modal>
|
|
)
|
|
}
|