Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions src/pages/articles/Articles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { IArticle } from '@/types/article.types';
import { getArticleArticlesSelector } from '@/redux/reducers/article.reducer';
import { createArticle, deleteArticle, goToArticle } from '@/redux/actions/article.action';
import Head from '@/components/Head';
import { getUserFullName } from '@/utils/user.utils';

const Articles = () => {
const articles = useSelector(getArticleArticlesSelector);
Expand All @@ -29,9 +30,9 @@ const Articles = () => {
dispatch(createArticle(values));
}

const handleDelete = (id: string) => {
dispatch(deleteArticle(id));
}
// const handleDelete = (id: string) => {
// dispatch(deleteArticle(id));
// }

const handlePreview = (id: string) => {
navigate(goToArticle(id));
Expand Down Expand Up @@ -60,19 +61,19 @@ const Articles = () => {
{article.objectId}
</TableCell>
<TableCell align="right">{article.title}</TableCell>
{/* <TableCell component="th" scope="row">
<TableCell component="th" scope="row">
{article.has("author") ? getUserFullName(article.get("author")) : "-"}
</TableCell> */}
</TableCell>
<TableCell align="right">
<IconButton color="info" onClick={() => handlePreview(article.objectId)}>
<FiEye />
</IconButton>
{/* <IconButton color="info" onClick={() => handleEdit(article.id)}>
<FiEdit2 />
</IconButton> */}
<IconButton color="error" onClick={() => handleDelete(article.objectId)}>
{/* <IconButton color="error" onClick={() => handleDelete(article.objectId)}>
<FiTrash2 />
</IconButton>
</IconButton> */}
</TableCell>
</TableRow>
))}
Expand Down
51 changes: 51 additions & 0 deletions src/pages/estimates/Estimate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { useTranslation } from 'react-i18next';
import { useState } from 'react';
import Head from '@/components/Head';
import Dialog from '@/components/Dialog';
import AddFab from '@/components/AddFab';
import EstimateForm from './EstimateForm';
import { EstimateInput } from '@/types/estimate.type';
import { SubmitHandler } from 'react-hook-form';
import { useDispatch } from 'react-redux';
import { createEstimate } from '@/redux/actions/estimate.action';

const ESTIMATE_FORM_ID = 'estimate-form-id';

const Estimate = () => {
const { t } = useTranslation();

const dispatch = useDispatch();


const [openFormDialog, setOpenFormDialog] = useState<boolean>(false);

const onSubmitHandler: SubmitHandler<EstimateInput> = values => {
dispatch(createEstimate(values));
};

const toggleDialog = () => setOpenFormDialog(!openFormDialog);

return (
<div>
<Head title={t('estimates')} />
<h1>Estimates</h1>
<AddFab onClick={toggleDialog} />
<Dialog
maxWidth="sm"
fullWidth
primaryButtonText={t('save')}
title={t('createEstimate')}
open={openFormDialog}
formId={ESTIMATE_FORM_ID}
toggle={toggleDialog}
>
<EstimateForm
formId={ESTIMATE_FORM_ID}
onSubmit={onSubmitHandler}
/>
</Dialog>
</div>
);
}

export default Estimate;
109 changes: 76 additions & 33 deletions src/pages/estimates/Estimates.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,92 @@
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell';
import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Paper from '@mui/material/Paper';
import { FiPlus, FiTrash2 , FiEye } from "react-icons/fi";

import { Fab, IconButton } from '@mui/material';
import { useNavigate } from '@tanstack/react-router';
import { useDispatch, useSelector } from 'react-redux';
import { useTranslation } from 'react-i18next';
import { useState } from 'react';
import Head from '@/components/Head';
import Dialog from '@/components/Dialog';
import AddFab from '@/components/AddFab';
import EstimateForm from './EstimateForm';
import { EstimateInput } from '@/types/estimate.type';
import { SubmitHandler } from 'react-hook-form';
import { useDispatch } from 'react-redux';
import { createEstimate } from '@/redux/actions/estimate.action';

const ESTIMATE_FORM_ID = 'estimate-form-id';
import { getEstimateEstimatesSelector } from '@/redux/reducers/estimate.reducer';
import { IEstimate } from '@/types/estimate.type';
import { loadEstimates, onEstimatesEnter } from '@/redux/actions/estimate.action';

const Estimates = () => {
const { t } = useTranslation();
const estimates = useSelector(getEstimateEstimatesSelector);

// const navigate = useNavigate();
const { t } = useTranslation();
const dispatch = useDispatch();

// const handleAddEstimate = async () => {
// const values = { title: `Article ${uid}` };
// dispatch(createEstimate(values));

// }
const getEstimate = async () => {
// const values = { url: `Estimate ${uid}` };
dispatch(loadEstimates());

const [openFormDialog, setOpenFormDialog] = useState<boolean>(false);
}

const onSubmitHandler: SubmitHandler<EstimateInput> = values => {
dispatch(createEstimate(values));
};
// const handleDelete = (id: string) => {
// dispatch(deleteArticle(id));
// }

const toggleDialog = () => setOpenFormDialog(!openFormDialog);
// const handlePreview = (id: string) => {
// navigate(goToArticle(id));
// }

return (
<div>
<Head title={t('estimates')} />
<h1>Estimates</h1>
<AddFab onClick={toggleDialog} />
<Dialog
maxWidth="sm"
fullWidth
primaryButtonText={t('save')}
title={t('createEstimate')}
open={openFormDialog}
formId={ESTIMATE_FORM_ID}
toggle={toggleDialog}
>
<EstimateForm
formId={ESTIMATE_FORM_ID}
onSubmit={onSubmitHandler}
/>
</Dialog>
<Head title={t('articles')} />
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Id</TableCell>
<TableCell align="right">Title</TableCell>
{/* <TableCell align="right">Author</TableCell> */}
<TableCell align="right">Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{estimates.map((estimate: IEstimate, index: number) => (
<TableRow
key={estimate.objectId + index}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell component="th" scope="row">
{estimate.objectId}
</TableCell>
<TableCell component="th" scope="row">
{estimate.url}
</TableCell>
<TableCell align="right">
{/* <IconButton color="info" onClick={() => handlePreview(article.objectId)}>
<FiEye />
</IconButton> */}
{/* <IconButton color="info" onClick={() => handleEdit(article.id)}>
<FiEdit2 />
</IconButton> */}
{/* <IconButton color="error" onClick={() => handleDelete(article.objectId)}>
<FiTrash2 />
</IconButton> */}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
{/* <Notification message={error} show={!!error} severity="error" /> */}
{/* <Fab color="primary" aria-label="add" onClick={handleAdd}>
<FiPlus />
</Fab> */}
</div>
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/pages/users/Users.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ const Users = () => {
}

const onSendEmailFormSubmit = async (values: SendEmailInput) => {
console.log('values: ', values);
// console.log('values: ', values);
if (!selectedUser) return;
await dispatch(sendEmailToUser(selectedUser, values));
handleCloseDialog();
Expand Down
28 changes: 26 additions & 2 deletions src/redux/actions/estimate.action.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
import Parse from "parse";
import Parse, { Attributes } from "parse";
import { PATH_NAMES } from "@/utils/pathnames";
import { setValues } from "@/utils/parse.utils";
import { actionWithLoader } from "@/utils/app.utils";
import { AppDispatch } from "../store";
import { setMessageSlice } from "../reducers/app.reducer";
import i18n from "@/config/i18n";
import { addEstimateToEstimateSlice, loadEstimatesSlice } from "../reducers/estimate.reducer";
// import { ParseResult } from "@/types/util.type";

const Estimate = Parse.Object.extend("Estimate");

const ESTIMATE_PROPERTIES = new Set(['url']);

export const loadEstimates = (): any => {
return actionWithLoader(async (dispatch: AppDispatch): Promise<void> => {
// user from BO
const result: any = await new Parse.Query(Estimate)
.withCount()
.notEqualTo('deleted', true)
.find();

const estimates = result.results.map((estimate: Attributes) => estimate.toJSON());

dispatch(loadEstimatesSlice(estimates));
});
};

export const createEstimate = (values: any): any => {
return actionWithLoader(async (dispatch: AppDispatch): Promise<void | undefined> => {
const estimate = new Estimate()
Expand All @@ -20,7 +36,15 @@ export const createEstimate = (values: any): any => {

const savedEstimate = await estimate.save();
dispatch(setMessageSlice(i18n.t('common:estimateCreatedSuccessfully')));
return savedEstimate;
dispatch(addEstimateToEstimateSlice((savedEstimate as Attributes).toJSON()));
// return savedEstimate;
});
};

export const onEstimatesEnter = (): any => {
return actionWithLoader(async (dispatch: AppDispatch): Promise<void> => {

dispatch(loadEstimates());
});
};

Expand Down
43 changes: 43 additions & 0 deletions src/redux/reducers/estimate.reducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { IEstimate, IEstimateState } from '@/types/estimate.type';

const initialState: IEstimateState = {
loading: false,
estimate: null,
estimates: [],
};

export const estimate = createSlice({
name: 'estimate',
initialState,
reducers: {
addEstimateToEstimateSlice: (state: IEstimateState, action: PayloadAction<IEstimate>) => {
state.estimates = [...state.estimates, action.payload];
},
loadEstimatesSlice: (state: IEstimateState, action: PayloadAction<IEstimate[]>) => {
state.estimates = action.payload;
}
},
});

export const {
addEstimateToEstimateSlice,
loadEstimatesSlice
} = estimate.actions;

// ---------------------------------------------- //
// ------------------ SELECTOR ------------------ //
// ---------------------------------------------- //
// NOTE: do not use RootState as state type to avoid import circular dependencies (from store.ts)
// we can not commit to git if there is circular dependencies
// export const getArticleSelector = (state: Record<string, any>): IArticleState => state.article;
// export const getArticleArticleSelector = (state: Record<string, any>): IArticle => state.article.article;
// export const getArticleLoadingSelector = (state: Record<string, any>): boolean => state.article.loading;
export const getEstimateEstimatesSelector = (state: Record<string, any>): IEstimate[] => state.estimate.estimates;
console.log('getEstimateEstimatesSelector------- : ', getEstimateEstimatesSelector );


// export const getArticleCountSelector = (state: Record<string, any>): number => state.article.count;


export default estimate.reducer;
2 changes: 2 additions & 0 deletions src/redux/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import appReducer from './reducers/app.reducer';
import roleReducer from './reducers/role.reducer';
import settingsReducer from './reducers/settings.reducer';
import userReducer from './reducers/user.reducer';
import estimateReducer from './reducers/estimate.reducer';
import articleReducer from './reducers/article.reducer';

const reducers = {
app: appReducer,
user: userReducer,
article: articleReducer,
estimate: estimateReducer,
settings: settingsReducer,
role: roleReducer,
};
Expand Down
14 changes: 12 additions & 2 deletions src/routes/protected/estimate.routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { Outlet, createRoute } from "@tanstack/react-router";

import { privateLayout } from "./private.routes";
import { PATH_NAMES } from "@/utils/pathnames";
import { onEnter } from "@/redux/actions/app.action";
import Estimates from "@/pages/estimates/Estimates";
import Estimate from "@/pages/estimates/Estimate";
import { onEstimatesEnter } from "@/redux/actions/estimate.action";

export const estimatesLayout = createRoute({
getParentRoute: () => privateLayout,
Expand All @@ -12,11 +15,18 @@ export const estimatesLayout = createRoute({

export const estimatesRoute = createRoute({
getParentRoute: () => estimatesLayout,
// beforeLoad: onEnter(onArticlesEnter),
beforeLoad: onEnter(onEstimatesEnter),
component: Estimates,
path: "/",
});

const estimateRoutes = [estimatesRoute];
export const estimateRoute = createRoute({
getParentRoute: () => estimatesLayout,
// beforeLoad: onEnter(onArticleEnter),
component: Estimate,
path: "$id",
});

const estimateRoutes = [estimatesRoute, estimateRoute];

export default estimateRoutes;
14 changes: 14 additions & 0 deletions src/types/estimate.type.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
import { z } from "zod";
import { Attributes } from "parse";
import { estimateSchema } from "@/validations/estimate.validation";

export type EstimateInput = z.infer<typeof estimateSchema>;

export interface IEstimate extends Attributes {
id: string;
url: string;
updatedAt?: string;
createdAt?: string;
}

export interface IEstimateState {
loading: boolean;
estimate: IEstimate | null;
estimates: IEstimate[];
}
Loading