fix bug invite user

This commit is contained in:
vergiLgood1 2025-03-23 06:28:39 +07:00
parent 0af8a9be0b
commit 398ca613ba
9 changed files with 54 additions and 32 deletions

View File

@ -52,7 +52,7 @@ function SubSubItemComponent({ item }: { item: SubSubItem }) {
asChild asChild
className={ className={
isActive isActive
? "bg-primary/10 active text-primary" ? "bg-primary/40 active text-primary"
: "" : ""
} }
> >
@ -77,7 +77,7 @@ function SubItemComponent({ item }: { item: SubItem }) {
asChild asChild
className={ className={
isActive isActive
? "bg-primary/10 active text-primary" ? "bg-primary/40 active text-primary"
: "" : ""
} }
> >
@ -99,7 +99,7 @@ function SubItemComponent({ item }: { item: SubItem }) {
<SidebarMenuButton <SidebarMenuButton
className={ className={
isActive isActive
? "bg-primary/10 active text-primary" ? "bg-primary/40 active text-primary"
: "" : ""
} }
> >
@ -138,7 +138,7 @@ function RecursiveNavItem({ item, index }: { item: NavItem; index: number }) {
asChild asChild
className={ className={
isActive isActive
? "bg-primary/10 active text-primary" ? "bg-primary/40 active text-primary"
: "" : ""
} }
> >
@ -166,7 +166,7 @@ function RecursiveNavItem({ item, index }: { item: NavItem; index: number }) {
tooltip={item.title} tooltip={item.title}
className={ className={
isActive isActive
? "bg-primary/10 active text-primary" ? "bg-primary/40 active text-primary"
: "" : ""
} }
> >

View File

@ -143,17 +143,21 @@ export default function UserManagement() {
user={detailUser} user={detailUser}
open={isSheetOpen} open={isSheetOpen}
onOpenChange={setIsSheetOpen} onOpenChange={setIsSheetOpen}
onUserUpdate={() => refetch()} onUserUpdate={() => { }}
/> />
)} )}
<AddUserDialog open={isAddUserOpen} onOpenChange={setIsAddUserOpen} onUserAdded={() => refetch()} /> <AddUserDialog
open={isAddUserOpen}
onOpenChange={setIsAddUserOpen}
onUserAdded={() => { }}
/>
<InviteUserDialog open={isInviteUserOpen} onOpenChange={setIsInviteUserOpen} onUserInvited={() => refetch()} /> <InviteUserDialog open={isInviteUserOpen} onOpenChange={setIsInviteUserOpen} onUserInvited={() => refetch()} />
{updateUser && ( {updateUser && (
<UserProfileSheet <UserProfileSheet
open={isUpdateOpen} open={isUpdateOpen}
onOpenChange={setIsUpdateOpen} onOpenChange={setIsUpdateOpen}
userData={updateUser} userData={updateUser}
onUserUpdated={() => refetch()} onUserUpdated={() => { }}
/> />
)} )}
</div> </div>

View File

@ -14,6 +14,7 @@ import { IBanDuration, IBanUserSchema, ICredentialsBanUserSchema } from '@/src/e
import { ICreateUserSchema } from '@/src/entities/models/users/create-user.model'; import { ICreateUserSchema } from '@/src/entities/models/users/create-user.model';
import { IUpdateUserSchema } from '@/src/entities/models/users/update-user.model'; import { IUpdateUserSchema } from '@/src/entities/models/users/update-user.model';
import { ICredentialsInviteUserSchema } from '@/src/entities/models/users/invite-user.model'; import { ICredentialsInviteUserSchema } from '@/src/entities/models/users/invite-user.model';
import { ICredentialGetUserByEmailSchema } from '@/src/entities/models/users/read-user.model';
export async function banUser(id: string, ban_duration: IBanDuration) { export async function banUser(id: string, ban_duration: IBanDuration) {
const instrumentationService = getInjection('IInstrumentationService'); const instrumentationService = getInjection('IInstrumentationService');
@ -116,9 +117,7 @@ export async function getCurrentUser() {
{ recordResponse: true }, { recordResponse: true },
async () => { async () => {
try { try {
const getCurrentUserController = getInjection( const getCurrentUserController = getInjection('IGetCurrentUserController');
'IGetCurrentUserController'
);
return await getCurrentUserController(); return await getCurrentUserController();
} catch (err) { } catch (err) {
@ -182,7 +181,7 @@ export async function getUserById(id: string) {
); );
} }
export async function getUserByEmail(email: string) { export async function getUserByEmail(credential: ICredentialGetUserByEmailSchema) {
const instrumentationService = getInjection('IInstrumentationService'); const instrumentationService = getInjection('IInstrumentationService');
return await instrumentationService.instrumentServerAction( return await instrumentationService.instrumentServerAction(
'getUserByEmail', 'getUserByEmail',
@ -192,7 +191,7 @@ export async function getUserByEmail(email: string) {
const getUserByEmailController = getInjection( const getUserByEmailController = getInjection(
'IGetUserByEmailController' 'IGetUserByEmailController'
); );
return await getUserByEmailController({ email }); return await getUserByEmailController({ email: credential.email });
} catch (err) { } catch (err) {

View File

@ -7,11 +7,14 @@ import { CreateUserSchema, defaulICreateUserSchemaValues, ICreateUserSchema } fr
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { defaulIInviteUserSchemaValues, IInviteUserSchema, InviteUserSchema } from '@/src/entities/models/users/invite-user.model'; import { defaulIInviteUserSchemaValues, IInviteUserSchema, InviteUserSchema } from '@/src/entities/models/users/invite-user.model';
import { useQueryClient } from '@tanstack/react-query';
export const useAddUserDialogHandler = ({ onUserAdded, onOpenChange }: { export const useAddUserDialogHandler = ({ onUserAdded, onOpenChange }: {
onUserAdded: () => void; onUserAdded: () => void;
onOpenChange: (open: boolean) => void; onOpenChange: (open: boolean) => void;
}) => { }) => {
const queryClient = useQueryClient();
const { createUser, isPending } = useCreateUserMutation(); const { createUser, isPending } = useCreateUserMutation();
const { const {
@ -38,7 +41,11 @@ export const useAddUserDialogHandler = ({ onUserAdded, onOpenChange }: {
await createUser(data, { await createUser(data, {
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["users"] });
toast.success("User created successfully."); toast.success("User created successfully.");
onUserAdded(); onUserAdded();
onOpenChange(false); onOpenChange(false);
reset(); reset();
@ -76,6 +83,7 @@ export const useInviteUserHandler = ({ onUserInvited, onOpenChange }: {
onOpenChange: (open: boolean) => void; onOpenChange: (open: boolean) => void;
}) => { }) => {
const queryClient = useQueryClient();
const { inviteUser, isPending } = useInviteUserMutation(); const { inviteUser, isPending } = useInviteUserMutation();
const { const {
@ -95,7 +103,11 @@ export const useInviteUserHandler = ({ onUserInvited, onOpenChange }: {
const onSubmit = handleSubmit(async (data) => { const onSubmit = handleSubmit(async (data) => {
await inviteUser(data, { await inviteUser(data, {
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["users"] });
toast.success("Invitation sent"); toast.success("Invitation sent");
onUserInvited(); onUserInvited();
onOpenChange(false); onOpenChange(false);
reset(); reset();

View File

@ -40,7 +40,7 @@ const useUsersAction = () => {
const getUserByEmailQuery = (email: string) => ({ const getUserByEmailQuery = (email: string) => ({
queryKey: ["user", "email", email], queryKey: ["user", "email", email],
queryFn: async () => await getUserByEmail(email) queryFn: async () => await getUserByEmail({ email })
}); });
const getUserByUsernameQuery = (username: string) => ({ const getUserByUsernameQuery = (username: string) => ({
@ -62,7 +62,7 @@ const useUsersAction = () => {
// Create functions that return configured hooks // Create functions that return configured hooks
const inviteUserMutation = useMutation({ const inviteUserMutation = useMutation({
mutationKey: ["inviteUser"], mutationKey: ["inviteUser"],
mutationFn: async (credential: ICredentialsInviteUserSchema) => await inviteUser(credential) mutationFn: async (credential: ICredentialsInviteUserSchema) => await inviteUser(credential)
}); });
const createUserMutation = useMutation({ const createUserMutation = useMutation({

View File

@ -17,7 +17,7 @@
--popover-foreground: 0 0% 10%; /* #1a1a1a */ --popover-foreground: 0 0% 10%; /* #1a1a1a */
/* Warna utama: hijau Supabase #006239 */ /* Warna utama: hijau Supabase #006239 */
--primary: 155% 100% 19%; /* #006239 */ --primary: 155 100% 19%; /* #006239 */
--primary-foreground: 0 0% 100%; /* #ffffff untuk kontras pada hijau */ --primary-foreground: 0 0% 100%; /* #ffffff untuk kontras pada hijau */
/* Sekunder: abu-abu terang untuk elemen pendukung */ /* Sekunder: abu-abu terang untuk elemen pendukung */
@ -41,13 +41,13 @@
--input: 0 0% 80%; /* #cccccc */ --input: 0 0% 80%; /* #cccccc */
/* Ring: sama dengan primary untuk fokus */ /* Ring: sama dengan primary untuk fokus */
--ring: 155% 100% 19%; /* #006239 */ --ring: 155 100% 19%; /* #006239 */
/* Radius: sudut membulat ringan */ /* Radius: sudut membulat ringan */
--radius: 0.5rem; --radius: 0.5rem;
/* Chart: gunakan hijau Supabase dan variasi */ /* Chart: gunakan hijau Supabase dan variasi */
--chart-1: 155% 100% 19%; /* #006239 */ --chart-1: 155 100% 19%; /* #006239 */
--chart-2: 160 60% 45%; /* sedikit lebih gelap */ --chart-2: 160 60% 45%; /* sedikit lebih gelap */
--chart-3: 165 55% 40%; --chart-3: 165 55% 40%;
--chart-4: 170 50% 35%; --chart-4: 170 50% 35%;
@ -56,12 +56,12 @@
/* Sidebar: mirip dengan kartu di mode terang */ /* Sidebar: mirip dengan kartu di mode terang */
--sidebar-background: 0 0% 98%; /* #fafafa */ --sidebar-background: 0 0% 98%; /* #fafafa */
--sidebar-foreground: 0 0% 10%; /* #1a1a1a */ --sidebar-foreground: 0 0% 10%; /* #1a1a1a */
--sidebar-primary: 155% 100% 19%; /* #006239 */ --sidebar-primary: 155 100% 19%; /* #006239 */
--sidebar-primary-foreground: 0 0% 100%; /* #ffffff */ --sidebar-primary-foreground: 0 0% 100%; /* #ffffff */
--sidebar-accent: 0 0% 96%; /* #f5f5f5 */ --sidebar-accent: 0 0% 96%; /* #f5f5f5 */
--sidebar-accent-foreground: 0 0% 10%; /* #1a1a1a */ --sidebar-accent-foreground: 0 0% 10%; /* #1a1a1a */
--sidebar-border: 0 0% 85%; /* #d9d9d9 */ --sidebar-border: 0 0% 85%; /* #d9d9d9 */
--sidebar-ring: 155% 100% 19%; /* #006239 */ --sidebar-ring: 155 100% 19%; /* #006239 */
} }
.dark { .dark {
@ -78,7 +78,7 @@
--popover-foreground: 0 0% 85%; /* #d9d9d9 */ --popover-foreground: 0 0% 85%; /* #d9d9d9 */
/* Warna utama: hijau Supabase tetap digunakan */ /* Warna utama: hijau Supabase tetap digunakan */
--primary: 155% 100% 19%; /* #006239 */ --primary: 155 100% 19%; /* #006239 */
--primary-foreground: 0 0% 100%; /* #ffffff */ --primary-foreground: 0 0% 100%; /* #ffffff */
/* Sekunder: abu-abu gelap untuk elemen pendukung */ /* Sekunder: abu-abu gelap untuk elemen pendukung */
@ -102,10 +102,10 @@
--input: 0 0% 20%; /* #333333 */ --input: 0 0% 20%; /* #333333 */
/* Ring: sama dengan primary */ /* Ring: sama dengan primary */
--ring: 155% 100% 19%; /* #006239 */ --ring: 155 100% 19%; /* #006239 */
/* Chart: sama seperti mode terang */ /* Chart: sama seperti mode terang */
--chart-1: 155% 100% 19%; /* #006239 */ --chart-1: 155 100% 19%; /* #006239 */
--chart-2: 160 60% 45%; --chart-2: 160 60% 45%;
--chart-3: 165 55% 40%; --chart-3: 165 55% 40%;
--chart-4: 170 50% 35%; --chart-4: 170 50% 35%;
@ -114,12 +114,12 @@
/* Sidebar: abu-abu gelap */ /* Sidebar: abu-abu gelap */
--sidebar-background: 0 0% 15%; /* #262626 */ --sidebar-background: 0 0% 15%; /* #262626 */
--sidebar-foreground: 0 0% 85%; /* #d9d9d9 */ --sidebar-foreground: 0 0% 85%; /* #d9d9d9 */
--sidebar-primary: 155% 100% 19%; /* #006239 */ --sidebar-primary: 155 100% 19%; /* #006239 */
--sidebar-primary-foreground: 0 0% 100%; /* #ffffff */ --sidebar-primary-foreground: 0 0% 100%; /* #ffffff */
--sidebar-accent: 0 0% 20%; /* #333333 */ --sidebar-accent: 0 0% 20%; /* #333333 */
--sidebar-accent-foreground: 0 0% 85%; /* #d9d9d9 */ --sidebar-accent-foreground: 0 0% 85%; /* #d9d9d9 */
--sidebar-border: 0 0% 25%; /* #404040 */ --sidebar-border: 0 0% 25%; /* #404040 */
--sidebar-ring: 155% 100% 19%; /* #006239 */ --sidebar-ring: 155 100% 19%; /* #006239 */
} }
} }

View File

@ -1,4 +1,4 @@
import { VerifyOtpSchema } from "@/src/entities/models/auth/verify-otp.model" import { IVerifyOtpSchema } from "@/src/entities/models/auth/verify-otp.model"
import { IUsersRepository } from "../../repositories/users.repository.interface" import { IUsersRepository } from "../../repositories/users.repository.interface"
import { IAuthenticationService } from "../../services/authentication.service.interface" import { IAuthenticationService } from "../../services/authentication.service.interface"
import { IInstrumentationService } from "../../services/instrumentation.service.interface" import { IInstrumentationService } from "../../services/instrumentation.service.interface"
@ -12,15 +12,18 @@ export const verifyOtpUseCase = (
instrumentationService: IInstrumentationService, instrumentationService: IInstrumentationService,
authenticationService: IAuthenticationService, authenticationService: IAuthenticationService,
usersRepository: IUsersRepository usersRepository: IUsersRepository
) => async (input: VerifyOtpSchema): Promise<void> => { ) => async (input: IVerifyOtpSchema): Promise<void> => {
return await instrumentationService.startSpan({ name: "verifyOtp Use Case", op: "function" }, return await instrumentationService.startSpan({ name: "verifyOtp Use Case", op: "function" },
async () => { async () => {
const user = await usersRepository.getUserByEmail(input.email)
const user = await usersRepository.getUserByEmail({ email: input.email })
if (!user) { if (!user) {
throw new NotFoundError("User not found") throw new NotFoundError("User not found")
} }
console.log("email = ", user.email)
await authenticationService.verifyOtp({ await authenticationService.verifyOtp({
email: input.email, email: input.email,
token: input.token token: input.token

View File

@ -14,11 +14,12 @@ export const inviteUserUseCase = (
) => async (credential: ICredentialsInviteUserSchema): Promise<IUserSchema> => { ) => async (credential: ICredentialsInviteUserSchema): Promise<IUserSchema> => {
return await instrumentationService.startSpan({ name: "inviteUser Use Case", op: "function" }, return await instrumentationService.startSpan({ name: "inviteUser Use Case", op: "function" },
async () => { async () => {
const existingUser = await usersRepository.getUserByEmail(credential)
if (existingUser) { // const existingUser = await usersRepository.getUserByEmail(credential)
throw new AuthenticationError("User already exists")
} // if (existingUser) {
// throw new AuthenticationError("User already exists")
// }
const invitedUser = await usersRepository.inviteUser(credential) const invitedUser = await usersRepository.inviteUser(credential)

View File

@ -18,6 +18,9 @@ export const verifyOtpController =
) => ) =>
async (input: Partial<z.infer<typeof verifyOtpInputSchema>>) => { async (input: Partial<z.infer<typeof verifyOtpInputSchema>>) => {
return await instrumentationService.startSpan({ name: "verifyOtp Controller" }, async () => { return await instrumentationService.startSpan({ name: "verifyOtp Controller" }, async () => {
// console.log("input", input)
const { data, error: inputParseError } = verifyOtpInputSchema.safeParse(input) const { data, error: inputParseError } = verifyOtpInputSchema.safeParse(input)
if (inputParseError) { if (inputParseError) {