Skip to content

Commit

Permalink
implemented feedback controller and service and added mutation hook f…
Browse files Browse the repository at this point in the history
…or sending feedback to backend
  • Loading branch information
radekm2000 committed Apr 1, 2024
1 parent d0f76bd commit 7d5c954
Show file tree
Hide file tree
Showing 17 changed files with 171 additions and 28 deletions.
12 changes: 12 additions & 0 deletions client/ecommerce/src/api/axios.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
Brand,
Conversation,
ExtendedUserWithProfileAndReviews,
Feedback,
FetchedNotifications,
LoginInput,
LoginResponseData,
Expand All @@ -21,6 +22,7 @@ import {
} from "../types/types";
import { RequestAccessTokenInterceptor } from "./request-access-token.interceptor";
import { ResponseOAuthInterceptor } from "./response-auth.interceptor";
import { FeedbackFormData } from "../components/FeedbackDialog";
const LIMIT = 5;
let baseUrl;
if (import.meta.env.VITE_PRODU == "false") {
Expand Down Expand Up @@ -290,3 +292,13 @@ export const getAdminNotifications = async () => {
const response = await axiosApi.get("admin-notifications");
return response.data as AdminNotification[];
};

export const addFeedback = async (dto: FeedbackFormData) => {
const response = await axiosApi.post("feedbacks", dto);
return response.data;
};

export const getFeedbacks = async () => {
const response = await axiosApi.get("feedbacks");
return response.data as Feedback[];
};
34 changes: 15 additions & 19 deletions client/ecommerce/src/components/FeedbackDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,17 @@ import {
Dialog,
DialogContent,
DialogTitle,
MenuItem,
Stack,
SxProps,
TextField,
Typography,
} from "@mui/material";
import { zodResolver } from "@hookform/resolvers/zod";

import { useState } from "react";
import { SubmitHandler, useForm } from "react-hook-form";
import { z } from "zod";
import { FormInputText } from "./form-component/FormInputText";
import { FormInputDropdown } from "./form-component/FormInputDropdown";
import { useAddFeedback } from "../hooks/useAddFeedback";

const featureTypes = ["other", "enhancement", "bug", "new feature"] as const;
export type featureType = (typeof featureTypes)[number];
Expand Down Expand Up @@ -46,24 +44,22 @@ export const FeedbackDialog = ({
alignItems: "flex-start",
},
};
const {
handleSubmit,
control,
setValue,
clearErrors,
formState: { errors },
} = useForm<FeedbackFormData>({
defaultValues: {
contactName: "",
description: "",
email: "",
featureType: "other",
},
resolver: zodResolver(FeedbackSchema),
});
const feedbackMutation = useAddFeedback();
const { mutate } = feedbackMutation;
const { handleSubmit, control, setValue, clearErrors } =
useForm<FeedbackFormData>({
defaultValues: {
contactName: "",
description: "",
email: "",
featureType: "other",
},
resolver: zodResolver(FeedbackSchema),
});

const onSubmit: SubmitHandler<FeedbackFormData> = (data) => {
console.log(data);
mutate(data);
onClose();
};
const handleClose = () => {
setValue("contactName", "");
Expand Down
6 changes: 4 additions & 2 deletions client/ecommerce/src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {
CardActionArea,
CardContent,
Container,
Dialog,
Tabs,
} from "@mui/material";
import { useMediaQuery } from "../hooks/useMediaQuery";
Expand Down Expand Up @@ -427,7 +426,10 @@ export const Navbar = () => {
</Button>

<Box sx={{ display: { xs: "none", md: "flex", gap: "5px" } }}>
<IconButton onClick={handleClickOpen}>
<IconButton
disableFocusRipple
onClick={handleClickOpen}
>
<FeedbackIcon />
</IconButton>
<FeedbackDialog
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Control, Controller, FieldValues } from "react-hook-form";
import { Control, Controller } from "react-hook-form";
import TextField from "@mui/material/TextField";
import { ReactNode } from "react";
import { FeedbackFormData, featureType } from "../FeedbackDialog";
import { FeedbackFormData } from "../FeedbackDialog";
export const FormInputText = ({
name,
control,
Expand All @@ -25,6 +25,7 @@ export const FormInputText = ({
helperText={error ? error.message : null}
error={!!error}
maxRows={maxRows}
spellCheck="false"
multiline={multiline}
onChange={onChange}
value={value}
Expand Down
1 change: 0 additions & 1 deletion client/ecommerce/src/components/pages/Product.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { useMutation } from "@tanstack/react-query";
import { useUserContext } from "../../contexts/UserContext";
import { useDeleteProduct } from "../../hooks/useDeleteProduct";
import { useAddAdminNotification } from "../../hooks/useAddAdminNotification";
import { useEffect } from "react";

export const Product = () => {
const stripe = useStripe();
Expand Down
3 changes: 1 addition & 2 deletions client/ecommerce/src/components/ratingSystem/ReviewForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ import {
import { UserWithAvatar } from "../../types/types";
import { AccountCircle } from "@mui/icons-material";
import { Controller, SubmitHandler, useForm } from "react-hook-form";
import { useEffect, useState } from "react";
import { useState } from "react";
import { useAddReview } from "../../hooks/useAddReview";
import { useMediaQuery } from "../../hooks/useMediaQuery";
import { z } from "zod";

type FormFields = {
rating: number;
Expand Down
19 changes: 19 additions & 0 deletions client/ecommerce/src/hooks/useAddFeedback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { addFeedback } from "../api/axios";
import toast from "react-hot-toast";
import { Feedback } from "../types/types";

export const useAddFeedback = () => {
const queryClient = useQueryClient();
return useMutation({
mutationKey: ["feedback", "add"],
mutationFn: addFeedback,
onSuccess: (data: Feedback) => {
queryClient.setQueryData(["feedback"], data);
toast.success("Feedback sent!");
},
onError: (err) => {
console.log(err);
},
});
};
9 changes: 9 additions & 0 deletions client/ecommerce/src/hooks/useFetchFeedbacks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useQuery } from "@tanstack/react-query";
import { getFeedbacks } from "../api/axios";

export const useFetchFeedbacks = () => {
return useQuery({
queryKey: ["feedbacks"],
queryFn: getFeedbacks,
});
};
2 changes: 2 additions & 0 deletions client/ecommerce/src/types/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Message } from "@mui/icons-material";
import { z } from "zod";
import { FeedbackFormData } from "../components/FeedbackDialog";

export type RegisterInput = {
username: string;
Expand Down Expand Up @@ -207,3 +208,4 @@ export type AdminNotification = {
userId?: number;
};

export type Feedback = FeedbackFormData & { createdAt: string };
2 changes: 2 additions & 0 deletions server/ecommerce/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { NodemailerModule } from './nodemailer/nodemailer.module';
import { StripeModule } from './stripe/stripe.module';
import { ReviewsModule } from './reviews/reviews.module';
import { AdminNotificationsModule } from './admin-notifications/admin-notifications.module';
import { FeedbacksModule } from './feedbacks/feedbacks.module';

@Module({
imports: [
Expand All @@ -35,6 +36,7 @@ import { AdminNotificationsModule } from './admin-notifications/admin-notificati
StripeModule,
ReviewsModule,
AdminNotificationsModule,
FeedbacksModule,
],
controllers: [AppController],
providers: [AppService],
Expand Down
20 changes: 20 additions & 0 deletions server/ecommerce/src/feedbacks/feedbacks.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Body, Controller, Get, Post, UsePipes } from '@nestjs/common';
import { FeedbacksService } from './feedbacks.service';
import { ZodValidationPipe } from 'src/utils/pipes/ZodValidationPipe';
import { FeedbackDto, FeedbackDtoSchema } from 'src/utils/dtos/feedback.dto';

@Controller('feedbacks')
export class FeedbacksController {
constructor(private readonly feedbacksService: FeedbacksService) {}

@Get()
async getFeedbacks() {
return await this.feedbacksService.getFeedbacks();
}

@Post()
@UsePipes(new ZodValidationPipe(FeedbackDtoSchema))
async addFeedback(@Body() dto: FeedbackDto) {
return await this.feedbacksService.addFeedback(dto);
}
}
12 changes: 12 additions & 0 deletions server/ecommerce/src/feedbacks/feedbacks.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { FeedbacksController } from './feedbacks.controller';
import { FeedbacksService } from './feedbacks.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Feedback } from 'src/utils/entities/feedback.entity';

@Module({
imports: [TypeOrmModule.forFeature([Feedback])],
controllers: [FeedbacksController],
providers: [FeedbacksService],
})
export class FeedbacksModule {}
22 changes: 22 additions & 0 deletions server/ecommerce/src/feedbacks/feedbacks.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Injectable } from '@nestjs/common';
import { feedbacksI } from './feedbacksInterface';
import { Feedback } from 'src/utils/entities/feedback.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { FeedbackDto } from 'src/utils/dtos/feedback.dto';

@Injectable()
export class FeedbacksService implements feedbacksI {
constructor(
@InjectRepository(Feedback)
private feedbackRepository: Repository<Feedback>,
) {}
async addFeedback(dto: FeedbackDto): Promise<Feedback> {
const newFeedback = this.feedbackRepository.create(dto);
return await this.feedbackRepository.save(newFeedback);
}

async getFeedbacks(): Promise<Feedback[]> {
return await this.feedbackRepository.find({});
}
}
7 changes: 7 additions & 0 deletions server/ecommerce/src/feedbacks/feedbacksInterface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { FeedbackDto } from 'src/utils/dtos/feedback.dto';
import { Feedback } from 'src/utils/entities/feedback.entity';

export interface feedbacksI {
getFeedbacks(): Promise<Feedback[]>;
addFeedback(dto: FeedbackDto): Promise<Feedback>;
}
15 changes: 15 additions & 0 deletions server/ecommerce/src/utils/dtos/feedback.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { z } from 'zod';

export const FeedbackDtoSchema = z.object({
featureType: z.union([
z.literal('other'),
z.literal('bug'),
z.literal('enhancement'),
z.literal('new feature'),
]),
email: z.string().email(),
contactName: z.string().min(1, 'Contact name is required'),
description: z.string().min(1, 'Description is required'),
});

export type FeedbackDto = z.infer<typeof FeedbackDtoSchema>;
3 changes: 1 addition & 2 deletions server/ecommerce/src/utils/dtos/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { User } from '../entities/user.entity';

export type Order = 'price_high_to_low' | 'price_low_to_high';

export type Brand =
Expand All @@ -25,3 +23,4 @@ export type Notification = {
senderId: number;
receiverId: number;
};
export type FeatureType = 'other' | 'enhancement' | 'bug' | 'new feature';
27 changes: 27 additions & 0 deletions server/ecommerce/src/utils/entities/feedback.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {
Column,
Entity,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';

@Entity()
export class Feedback {
@PrimaryGeneratedColumn()
id: number;

@Column()
featureType: 'other' | 'enhancement' | 'bug' | 'new feature';

@Column()
email: string;

@Column()
contactName: string;

@Column()
description: string;

@UpdateDateColumn()
createdAt: Date;
}

0 comments on commit 7d5c954

Please sign in to comment.