├── .eslintrc.cjs ├── .gitignore ├── README.md ├── db.json ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── images │ ├── Real estate agent offer house represented by model.webp │ ├── agent-estate.jpg │ └── logo_of_REMS.avif └── logo.png ├── src ├── App.tsx ├── admin │ ├── components │ │ ├── agents │ │ │ ├── AgentDrawer.tsx │ │ │ ├── AgentForm.tsx │ │ │ ├── AgentList.tsx │ │ │ └── styles.css │ │ ├── appointments │ │ │ └── Appointments.tsx │ │ ├── clients │ │ │ ├── ClientDrawer.tsx │ │ │ ├── ClientForm.tsx │ │ │ ├── ClientList.tsx │ │ │ └── styles.css │ │ ├── dashboard │ │ │ ├── HomePage.tsx │ │ │ └── LineChartDashboard.tsx │ │ ├── properties │ │ │ ├── PropertyDetail.tsx │ │ │ └── PropertyList.tsx │ │ └── transactions │ │ │ └── TransactionList.tsx │ └── layouts │ │ ├── DashboardLayout.tsx │ │ ├── context │ │ └── DashboradContext.tsx │ │ ├── header │ │ └── DashboardHeader.tsx │ │ └── sidebar │ │ ├── DashboardSidebar.tsx │ │ └── style.css ├── agents │ ├── agent-services │ │ ├── ScrollToTop.ts │ │ └── propertyFilterSearch.ts │ ├── appointment │ │ └── AgentAppointment.tsx │ ├── defalult-page │ │ ├── AgentDefaultPage.tsx │ │ ├── AgentFooter.tsx │ │ ├── NavCards.tsx │ │ ├── RecentPosts.tsx │ │ ├── VinylSearch.tsx │ │ ├── YangonRecommended.tsx │ │ └── components │ │ │ └── PropertyCard.tsx │ ├── header │ │ └── AgentHeader.tsx │ ├── property-crud │ │ ├── PropertiesCRUD.tsx │ │ ├── PropertiesForm.tsx │ │ ├── PropertyCard.tsx │ │ ├── PropertyDrawer.tsx │ │ ├── db.ts │ │ └── styles.css │ ├── property-list │ │ ├── AgentPropertyList.tsx │ │ ├── DetailPage.tsx │ │ ├── Dropdowns │ │ │ ├── FilterDropdown.tsx │ │ │ ├── PriceDropDown.tsx │ │ │ └── SimpleFilterDropDown.tsx │ │ ├── Filters │ │ │ ├── BaAndBdsFilter.tsx │ │ │ ├── CityFilter.tsx │ │ │ ├── HomeTypeFilter.tsx │ │ │ └── PriceRangeFilter.tsx │ │ ├── Flyout.tsx │ │ ├── PropertyDetails.tsx │ │ ├── Reviews.tsx │ │ ├── SingleCard.tsx │ │ └── data-for-agent │ │ │ ├── propertyData.ts │ │ │ └── reviewsData.ts │ └── transactions │ │ └── AgentTransactions.tsx ├── app │ ├── hook.ts │ └── store.ts ├── client │ ├── components │ │ ├── appointment │ │ │ ├── AppointHistory.tsx │ │ │ ├── Appointment.tsx │ │ │ ├── AppointmentForm.tsx │ │ │ ├── AppointmentHistoryList.tsx │ │ │ ├── PickDate.tsx │ │ │ └── PickTime.tsx │ │ ├── property │ │ │ ├── Checkbox.tsx │ │ │ ├── CheckboxGroup.tsx │ │ │ ├── Container.tsx │ │ │ ├── FilterHome.tsx │ │ │ ├── Home.tsx │ │ │ ├── PriceRangeSlider.tsx │ │ │ ├── PropertyById.tsx │ │ │ ├── PropertyCard.tsx │ │ │ └── PropertyGroup.tsx │ │ ├── review │ │ │ ├── RatingReview.tsx │ │ │ └── Review.tsx │ │ └── transaction │ │ │ ├── Transaction.tsx │ │ │ ├── TransactionCreateForm.tsx │ │ │ └── TransactionSummary.tsx │ ├── db │ │ ├── data.ts │ │ └── mock.json │ ├── layouts │ │ └── Navbar.tsx │ └── style │ │ └── priceRange.css ├── error │ └── Error.tsx ├── errorPage │ └── ErrorPage.tsx ├── index.css ├── login │ ├── AgentRegister.tsx │ ├── ClientRegister.tsx │ ├── Login.tsx │ ├── Register.tsx │ └── login-context │ │ └── AuthContext.tsx ├── main.tsx ├── routes │ └── Router.tsx ├── services │ ├── admin │ │ └── api │ │ │ ├── agentApi.ts │ │ │ ├── appointmentApi.ts │ │ │ ├── clientApi.ts │ │ │ ├── dashboardApi.ts │ │ │ ├── propertiesApi.ts │ │ │ └── transactionsApi.ts │ ├── agent │ │ └── api │ │ │ ├── appointment.ts │ │ │ ├── getAgentApiSlice.ts │ │ │ ├── propertyApiSlice.ts │ │ │ └── text.ts │ ├── client │ │ ├── api │ │ │ ├── Review.ts │ │ │ ├── appointmentApi.ts │ │ │ ├── propertyApi.ts │ │ │ ├── text.ts │ │ │ ├── transactionApi.ts │ │ │ └── userIdApi.ts │ │ └── features │ │ │ ├── appointmentSlice.ts │ │ │ ├── currentPageSlice.ts │ │ │ └── idSlice.ts │ └── login-interceptors │ │ └── axios.ts ├── type │ └── type.ts └── vite-env.d.ts ├── tailwind.config.js ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Dependency directories 9 | node_modules/ 10 | 11 | # Environment files 12 | .env 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | # Build output 19 | build/ 20 | dist/ 21 | 22 | # Cache directories 23 | .cache/ 24 | .next/ 25 | out/ 26 | public/static/ 27 | 28 | # Testing 29 | coverage/ 30 | junit.xml 31 | test-results.xml 32 | 33 | # Miscellaneous 34 | .DS_Store 35 | Thumbs.db 36 | .idea/ 37 | .vscode/ 38 | *.swp 39 | *~.swp 40 | *.swo 41 | *.swn 42 | 43 | # Editor directories and files 44 | /.vscode/ 45 | /.vscode/* 46 | *.code-workspace 47 | .idea/ 48 | *.sublime-project 49 | *.sublime-workspace 50 | 51 | # React Native specific 52 | .expo/ 53 | .expo-shared/ 54 | 55 | # Yarn specific 56 | .yarn/* 57 | !.yarn/patches 58 | !.yarn/plugins 59 | !.yarn/sdks 60 | !.yarn/versions 61 | .pnp.* 62 | .pnp/ 63 | 64 | # SASS cache 65 | .sass-cache/ 66 | 67 | # Optional npm cache directory 68 | .npm 69 | 70 | # eslint cache 71 | .eslintcache 72 | 73 | # Optional REACT-NATIVE CACHES 74 | react-native/packager-info.json 75 | react-native/metro-cache/ 76 | 77 | .vs -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 13 | 14 | - Configure the top-level `parserOptions` property like this: 15 | 16 | ```js 17 | export default { 18 | // other rules... 19 | parserOptions: { 20 | ecmaVersion: 'latest', 21 | sourceType: 'module', 22 | project: ['./tsconfig.json', './tsconfig.node.json'], 23 | tsconfigRootDir: __dirname, 24 | }, 25 | } 26 | ``` 27 | 28 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` 29 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked` 30 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list 31 | 32 | json-server --watch ./src/client/db/mock.json 33 | -------------------------------------------------------------------------------- /db.json: -------------------------------------------------------------------------------- 1 | { 2 | "appointment": [ 3 | { 4 | "appointmentId": "1", 5 | "agentName": "John Doe", 6 | "propertyName": "Sunny Apartments", 7 | "appointmentDate": "2024-07-1", 8 | "appointmentTime": "10:00 AM", 9 | "status": "Pending", 10 | "notes": "Client prefers morning appointments." 11 | }, 12 | { 13 | "appointmentId": "2", 14 | "agentName": "Jane Smith", 15 | "propertyName": "Green Meadows", 16 | "appointmentDate": "2024-07-22", 17 | "appointmentTime": "02:00 PM", 18 | "status": "Confirmed", 19 | "notes": "Bring property documents." 20 | }, 21 | { 22 | "appointmentId": "3", 23 | "agentName": "Michael Brown", 24 | "propertyName": "Ocean View Villas", 25 | "appointmentDate": "2024-07-23", 26 | "appointmentTime": "11:00 AM", 27 | "status": "Done", 28 | "notes": "Client rescheduled for next week." 29 | }, 30 | { 31 | "appointmentId": "4", 32 | "agentName": "Emily Davis", 33 | "propertyName": "Maple Residences", 34 | "appointmentDate": "2024-07-24", 35 | "appointmentTime": "09:00 AM", 36 | "status": "Done", 37 | "notes": "Awaiting client confirmation." 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | REMS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rems_react", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc -b && vite build", 9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@ant-design/icons": "^5.3.7", 14 | "@reduxjs/toolkit": "^2.2.6", 15 | "@types/react-router": "^5.1.20", 16 | "@types/react-router-dom": "^5.3.3", 17 | "antd": "^5.19.1", 18 | "axios": "^1.7.7", 19 | "framer-motion": "^11.5.4", 20 | "jwt-decode": "^4.0.0", 21 | "react": "^18.3.1", 22 | "react-dom": "^18.3.1", 23 | "react-icons": "^5.3.0", 24 | "react-images-uploading": "^3.1.7", 25 | "react-redux": "^9.1.2", 26 | "react-router": "^6.24.1", 27 | "react-router-dom": "^6.24.1", 28 | "react-star-rating": "^1.4.2", 29 | "recharts": "^2.12.7", 30 | "redux-persist": "^6.0.0", 31 | "sonner": "^1.5.0", 32 | "swiper": "^11.1.14" 33 | }, 34 | "devDependencies": { 35 | "@types/react": "^18.3.3", 36 | "@types/react-dom": "^18.3.0", 37 | "@typescript-eslint/eslint-plugin": "^7.13.1", 38 | "@typescript-eslint/parser": "^7.13.1", 39 | "@vitejs/plugin-react": "^4.3.1", 40 | "autoprefixer": "^10.4.19", 41 | "eslint": "^8.57.0", 42 | "eslint-plugin-react-hooks": "^4.6.2", 43 | "eslint-plugin-react-refresh": "^0.4.7", 44 | "postcss": "^8.4.39", 45 | "tailwindcss": "^3.4.4", 46 | "typescript": "^5.2.2", 47 | "vite": "^5.3.1" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/images/Real estate agent offer house represented by model.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/one-project-one-month/rems_react/18eeeda10a34cc3ebe073c7da9a52b7a876a944b/public/images/Real estate agent offer house represented by model.webp -------------------------------------------------------------------------------- /public/images/agent-estate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/one-project-one-month/rems_react/18eeeda10a34cc3ebe073c7da9a52b7a876a944b/public/images/agent-estate.jpg -------------------------------------------------------------------------------- /public/images/logo_of_REMS.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/one-project-one-month/rems_react/18eeeda10a34cc3ebe073c7da9a52b7a876a944b/public/images/logo_of_REMS.avif -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/one-project-one-month/rems_react/18eeeda10a34cc3ebe073c7da9a52b7a876a944b/public/logo.png -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { ConfigProvider } from "antd"; 2 | import Router from "./routes/Router"; 3 | import { Toaster } from "sonner"; 4 | import { AuthProvider } from "./login/login-context/AuthContext"; 5 | import { Provider } from "react-redux"; 6 | import { persistor, store } from "./app/store"; 7 | import { PersistGate } from "redux-persist/integration/react"; 8 | 9 | export default function App() { 10 | return ( 11 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /src/admin/components/agents/AgentDrawer.tsx: -------------------------------------------------------------------------------- 1 | import { Drawer } from "antd"; 2 | import AgentForm from "./AgentForm"; 3 | import "./styles.css"; 4 | import { Agent } from "../../../type/type"; 5 | 6 | interface Props { 7 | onClose: () => void; 8 | open: boolean; 9 | records: Agent | null; 10 | refetch: () => void; 11 | } 12 | 13 | const AgentDrawer = ({ onClose, open, records, refetch }: Props) => { 14 | return ( 15 | <> 16 | 26 | 31 | 32 | 33 | ); 34 | }; 35 | 36 | export default AgentDrawer; 37 | -------------------------------------------------------------------------------- /src/admin/components/agents/AgentForm.tsx: -------------------------------------------------------------------------------- 1 | import { Button, ConfigProvider, Form, Input, Space } from "antd"; 2 | import { RuleObject } from "antd/lib/form"; 3 | import { StoreValue } from "rc-field-form/lib/interface"; 4 | import { useEffect } from "react"; 5 | import { Agent } from "../../../type/type"; 6 | import { toast } from "sonner"; 7 | import { 8 | useCreateAgentMutation, 9 | useUpdateAgentByIdMutation, 10 | } from "../../../services/admin/api/agentApi"; 11 | 12 | interface Props { 13 | onClose: () => void; 14 | initialValues: Agent | null; 15 | refetch: () => void; 16 | } 17 | 18 | const AgentForm = ({ onClose, initialValues, refetch }: Props) => { 19 | const [createAgent] = useCreateAgentMutation(); 20 | const [updateAgent] = useUpdateAgentByIdMutation(); 21 | const [form] = Form.useForm(); 22 | 23 | const { TextArea } = Input; 24 | 25 | useEffect(() => { 26 | if (initialValues) { 27 | form.setFieldsValue(initialValues); 28 | } else { 29 | form.resetFields(); 30 | } 31 | }, [initialValues, form]); 32 | 33 | const onFinish = async () => { 34 | const values = form.getFieldsValue(); 35 | const passwordWithValue = { ...values, password: "password123" }; 36 | 37 | try { 38 | if (initialValues && initialValues.agentId) { 39 | // Update agent if initialValues is provided 40 | await updateAgent({ 41 | data: passwordWithValue, 42 | id: initialValues.agentId, 43 | }); 44 | refetch(); 45 | toast.success("Agent update successfully"); 46 | onClose(); 47 | } else { 48 | // Create new agent if no initialValues 49 | await createAgent(passwordWithValue).unwrap(); 50 | refetch(); 51 | toast.success("Agent create successfully"); 52 | onClose(); 53 | } 54 | onClose(); 55 | } catch (error) { 56 | toast.error("Error submitting form"); 57 | } 58 | }; 59 | 60 | const validatePhoneNumber = (_: RuleObject, value: StoreValue) => { 61 | const phoneRegex = /^09\d{7,9}$/; 62 | if (!value) { 63 | return Promise.reject(new Error("Please enter your phone number.")); 64 | } 65 | if (!phoneRegex.test(value)) { 66 | return Promise.reject( 67 | new Error("Please enter a valid Myanmar phone number (09xxxxxxxxx).") 68 | ); 69 | } 70 | return Promise.resolve(); 71 | }; 72 | 73 | return ( 74 | ( 77 | <> 78 | {label} 79 | {required && ( 80 | * 81 | )} 82 | 83 | ), 84 | }}> 85 |
90 | 94 | 95 | 96 | 100 | 101 | 102 | 106 | 107 | 108 | 115 | 116 | 117 | 121 | 125 | 126 | 127 | 128 |