[
  {
    "path": "api/.gitignore",
    "content": "node_modules\n.env"
  },
  {
    "path": "api/app.js",
    "content": "import express from \"express\";\nimport cors from \"cors\";\nimport cookieParser from \"cookie-parser\";\nimport authRoute from \"./routes/auth.route.js\";\nimport postRoute from \"./routes/post.route.js\";\nimport testRoute from \"./routes/test.route.js\";\nimport userRoute from \"./routes/user.route.js\";\nimport chatRoute from \"./routes/chat.route.js\";\nimport messageRoute from \"./routes/message.route.js\";\n\nconst app = express();\n\napp.use(cors({ origin: process.env.CLIENT_URL, credentials: true }));\napp.use(express.json());\napp.use(cookieParser());\n\napp.use(\"/api/auth\", authRoute);\napp.use(\"/api/users\", userRoute);\napp.use(\"/api/posts\", postRoute);\napp.use(\"/api/test\", testRoute);\napp.use(\"/api/chats\", chatRoute);\napp.use(\"/api/messages\", messageRoute);\n\napp.listen(8800, () => {\n  console.log(\"Server is running!\");\n});\n"
  },
  {
    "path": "api/controllers/auth.controller.js",
    "content": "import bcrypt from \"bcrypt\";\nimport jwt from \"jsonwebtoken\";\nimport prisma from \"../lib/prisma.js\";\n\nexport const register = async (req, res) => {\n  const { username, email, password } = req.body;\n\n  try {\n    // HASH THE PASSWORD\n\n    const hashedPassword = await bcrypt.hash(password, 10);\n\n    console.log(hashedPassword);\n\n    // CREATE A NEW USER AND SAVE TO DB\n    const newUser = await prisma.user.create({\n      data: {\n        username,\n        email,\n        password: hashedPassword,\n      },\n    });\n\n    console.log(newUser);\n\n    res.status(201).json({ message: \"User created successfully\" });\n  } catch (err) {\n    console.log(err);\n    res.status(500).json({ message: \"Failed to create user!\" });\n  }\n};\n\nexport const login = async (req, res) => {\n  const { username, password } = req.body;\n\n  try {\n    // CHECK IF THE USER EXISTS\n\n    const user = await prisma.user.findUnique({\n      where: { username },\n    });\n\n    if (!user) return res.status(400).json({ message: \"Invalid Credentials!\" });\n\n    // CHECK IF THE PASSWORD IS CORRECT\n\n    const isPasswordValid = await bcrypt.compare(password, user.password);\n\n    if (!isPasswordValid)\n      return res.status(400).json({ message: \"Invalid Credentials!\" });\n\n    // GENERATE COOKIE TOKEN AND SEND TO THE USER\n\n    // res.setHeader(\"Set-Cookie\", \"test=\" + \"myValue\").json(\"success\")\n    const age = 1000 * 60 * 60 * 24 * 7;\n\n    const token = jwt.sign(\n      {\n        id: user.id,\n        isAdmin: false,\n      },\n      process.env.JWT_SECRET_KEY,\n      { expiresIn: age }\n    );\n\n    const { password: userPassword, ...userInfo } = user;\n\n    res\n      .cookie(\"token\", token, {\n        httpOnly: true,\n        // secure:true,\n        maxAge: age,\n      })\n      .status(200)\n      .json(userInfo);\n  } catch (err) {\n    console.log(err);\n    res.status(500).json({ message: \"Failed to login!\" });\n  }\n};\n\nexport const logout = (req, res) => {\n  res.clearCookie(\"token\").status(200).json({ message: \"Logout Successful\" });\n};\n"
  },
  {
    "path": "api/controllers/chat.controller.js",
    "content": "import prisma from \"../lib/prisma.js\";\n\nexport const getChats = async (req, res) => {\n  const tokenUserId = req.userId;\n\n  try {\n    const chats = await prisma.chat.findMany({\n      where: {\n        userIDs: {\n          hasSome: [tokenUserId],\n        },\n      },\n    });\n\n    for (const chat of chats) {\n      const receiverId = chat.userIDs.find((id) => id !== tokenUserId);\n\n      const receiver = await prisma.user.findUnique({\n        where: {\n          id: receiverId,\n        },\n        select: {\n          id: true,\n          username: true,\n          avatar: true,\n        },\n      });\n      chat.receiver = receiver;\n    }\n\n    res.status(200).json(chats);\n  } catch (err) {\n    console.log(err);\n    res.status(500).json({ message: \"Failed to get chats!\" });\n  }\n};\n\nexport const getChat = async (req, res) => {\n  const tokenUserId = req.userId;\n\n  try {\n    const chat = await prisma.chat.findUnique({\n      where: {\n        id: req.params.id,\n        userIDs: {\n          hasSome: [tokenUserId],\n        },\n      },\n      include: {\n        messages: {\n          orderBy: {\n            createdAt: \"asc\",\n          },\n        },\n      },\n    });\n\n    await prisma.chat.update({\n      where: {\n        id: req.params.id,\n      },\n      data: {\n        seenBy: {\n          push: [tokenUserId],\n        },\n      },\n    });\n    res.status(200).json(chat);\n  } catch (err) {\n    console.log(err);\n    res.status(500).json({ message: \"Failed to get chat!\" });\n  }\n};\n\nexport const addChat = async (req, res) => {\n  const tokenUserId = req.userId;\n  try {\n    const newChat = await prisma.chat.create({\n      data: {\n        userIDs: [tokenUserId, req.body.receiverId],\n      },\n    });\n    res.status(200).json(newChat);\n  } catch (err) {\n    console.log(err);\n    res.status(500).json({ message: \"Failed to add chat!\" });\n  }\n};\n\nexport const readChat = async (req, res) => {\n  const tokenUserId = req.userId;\n\n  \n  try {\n    const chat = await prisma.chat.update({\n      where: {\n        id: req.params.id,\n        userIDs: {\n          hasSome: [tokenUserId],\n        },\n      },\n      data: {\n        seenBy: {\n          set: [tokenUserId],\n        },\n      },\n    });\n    res.status(200).json(chat);\n  } catch (err) {\n    console.log(err);\n    res.status(500).json({ message: \"Failed to read chat!\" });\n  }\n};\n"
  },
  {
    "path": "api/controllers/message.controller.js",
    "content": "import prisma from \"../lib/prisma.js\";\n\nexport const addMessage = async (req, res) => {\n  const tokenUserId = req.userId;\n  const chatId = req.params.chatId;\n  const text = req.body.text;\n\n  try {\n    const chat = await prisma.chat.findUnique({\n      where: {\n        id: chatId,\n        userIDs: {\n          hasSome: [tokenUserId],\n        },\n      },\n    });\n\n    if (!chat) return res.status(404).json({ message: \"Chat not found!\" });\n\n    const message = await prisma.message.create({\n      data: {\n        text,\n        chatId,\n        userId: tokenUserId,\n      },\n    });\n\n    await prisma.chat.update({\n      where: {\n        id: chatId,\n      },\n      data: {\n        seenBy: [tokenUserId],\n        lastMessage: text,\n      },\n    });\n\n    res.status(200).json(message);\n  } catch (err) {\n    console.log(err);\n    res.status(500).json({ message: \"Failed to add message!\" });\n  }\n};\n"
  },
  {
    "path": "api/controllers/post.controller.js",
    "content": "import prisma from \"../lib/prisma.js\";\nimport jwt from \"jsonwebtoken\";\n\nexport const getPosts = async (req, res) => {\n  const query = req.query;\n\n  try {\n    const posts = await prisma.post.findMany({\n      where: {\n        city: query.city || undefined,\n        type: query.type || undefined,\n        property: query.property || undefined,\n        bedroom: parseInt(query.bedroom) || undefined,\n        price: {\n          gte: parseInt(query.minPrice) || undefined,\n          lte: parseInt(query.maxPrice) || undefined,\n        },\n      },\n    });\n\n    // setTimeout(() => {\n    res.status(200).json(posts);\n    // }, 3000);\n  } catch (err) {\n    console.log(err);\n    res.status(500).json({ message: \"Failed to get posts\" });\n  }\n};\n\nexport const getPost = async (req, res) => {\n  const id = req.params.id;\n  try {\n    const post = await prisma.post.findUnique({\n      where: { id },\n      include: {\n        postDetail: true,\n        user: {\n          select: {\n            username: true,\n            avatar: true,\n          },\n        },\n      },\n    });\n\n    const token = req.cookies?.token;\n\n    if (token) {\n      jwt.verify(token, process.env.JWT_SECRET_KEY, async (err, payload) => {\n        if (!err) {\n          const saved = await prisma.savedPost.findUnique({\n            where: {\n              userId_postId: {\n                postId: id,\n                userId: payload.id,\n              },\n            },\n          });\n          res.status(200).json({ ...post, isSaved: saved ? true : false });\n        }\n      });\n    }\n    res.status(200).json({ ...post, isSaved: false });\n  } catch (err) {\n    console.log(err);\n    res.status(500).json({ message: \"Failed to get post\" });\n  }\n};\n\nexport const addPost = async (req, res) => {\n  const body = req.body;\n  const tokenUserId = req.userId;\n\n  try {\n    const newPost = await prisma.post.create({\n      data: {\n        ...body.postData,\n        userId: tokenUserId,\n        postDetail: {\n          create: body.postDetail,\n        },\n      },\n    });\n    res.status(200).json(newPost);\n  } catch (err) {\n    console.log(err);\n    res.status(500).json({ message: \"Failed to create post\" });\n  }\n};\n\nexport const updatePost = async (req, res) => {\n  try {\n    res.status(200).json();\n  } catch (err) {\n    console.log(err);\n    res.status(500).json({ message: \"Failed to update posts\" });\n  }\n};\n\nexport const deletePost = async (req, res) => {\n  const id = req.params.id;\n  const tokenUserId = req.userId;\n\n  try {\n    const post = await prisma.post.findUnique({\n      where: { id },\n    });\n\n    if (post.userId !== tokenUserId) {\n      return res.status(403).json({ message: \"Not Authorized!\" });\n    }\n\n    await prisma.post.delete({\n      where: { id },\n    });\n\n    res.status(200).json({ message: \"Post deleted\" });\n  } catch (err) {\n    console.log(err);\n    res.status(500).json({ message: \"Failed to delete post\" });\n  }\n};\n"
  },
  {
    "path": "api/controllers/test.controller.js",
    "content": "import jwt from \"jsonwebtoken\";\n\nexport const shouldBeLoggedIn = async (req, res) => {\n  console.log(req.userId)\n  res.status(200).json({ message: \"You are Authenticated\" });\n};\n\nexport const shouldBeAdmin = async (req, res) => {\n  const token = req.cookies.token;\n\n  if (!token) return res.status(401).json({ message: \"Not Authenticated!\" });\n\n  jwt.verify(token, process.env.JWT_SECRET_KEY, async (err, payload) => {\n    if (err) return res.status(403).json({ message: \"Token is not Valid!\" });\n    if (!payload.isAdmin) {\n      return res.status(403).json({ message: \"Not authorized!\" });\n    }\n  });\n\n  res.status(200).json({ message: \"You are Authenticated\" });\n};\n"
  },
  {
    "path": "api/controllers/user.controller.js",
    "content": "import prisma from \"../lib/prisma.js\";\nimport bcrypt from \"bcrypt\";\n\nexport const getUsers = async (req, res) => {\n  try {\n    const users = await prisma.user.findMany();\n    res.status(200).json(users);\n  } catch (err) {\n    console.log(err);\n    res.status(500).json({ message: \"Failed to get users!\" });\n  }\n};\n\nexport const getUser = async (req, res) => {\n  const id = req.params.id;\n  try {\n    const user = await prisma.user.findUnique({\n      where: { id },\n    });\n    res.status(200).json(user);\n  } catch (err) {\n    console.log(err);\n    res.status(500).json({ message: \"Failed to get user!\" });\n  }\n};\n\nexport const updateUser = async (req, res) => {\n  const id = req.params.id;\n  const tokenUserId = req.userId;\n  const { password, avatar, ...inputs } = req.body;\n\n  if (id !== tokenUserId) {\n    return res.status(403).json({ message: \"Not Authorized!\" });\n  }\n\n  let updatedPassword = null;\n  try {\n    if (password) {\n      updatedPassword = await bcrypt.hash(password, 10);\n    }\n\n    const updatedUser = await prisma.user.update({\n      where: { id },\n      data: {\n        ...inputs,\n        ...(updatedPassword && { password: updatedPassword }),\n        ...(avatar && { avatar }),\n      },\n    });\n\n    const { password: userPassword, ...rest } = updatedUser;\n\n    res.status(200).json(rest);\n  } catch (err) {\n    console.log(err);\n    res.status(500).json({ message: \"Failed to update users!\" });\n  }\n};\n\nexport const deleteUser = async (req, res) => {\n  const id = req.params.id;\n  const tokenUserId = req.userId;\n\n  if (id !== tokenUserId) {\n    return res.status(403).json({ message: \"Not Authorized!\" });\n  }\n\n  try {\n    await prisma.user.delete({\n      where: { id },\n    });\n    res.status(200).json({ message: \"User deleted\" });\n  } catch (err) {\n    console.log(err);\n    res.status(500).json({ message: \"Failed to delete users!\" });\n  }\n};\n\nexport const savePost = async (req, res) => {\n  const postId = req.body.postId;\n  const tokenUserId = req.userId;\n\n  try {\n    const savedPost = await prisma.savedPost.findUnique({\n      where: {\n        userId_postId: {\n          userId: tokenUserId,\n          postId,\n        },\n      },\n    });\n\n    if (savedPost) {\n      await prisma.savedPost.delete({\n        where: {\n          id: savedPost.id,\n        },\n      });\n      res.status(200).json({ message: \"Post removed from saved list\" });\n    } else {\n      await prisma.savedPost.create({\n        data: {\n          userId: tokenUserId,\n          postId,\n        },\n      });\n      res.status(200).json({ message: \"Post saved\" });\n    }\n  } catch (err) {\n    console.log(err);\n    res.status(500).json({ message: \"Failed to delete users!\" });\n  }\n};\n\nexport const profilePosts = async (req, res) => {\n  const tokenUserId = req.userId;\n  try {\n    const userPosts = await prisma.post.findMany({\n      where: { userId: tokenUserId },\n    });\n    const saved = await prisma.savedPost.findMany({\n      where: { userId: tokenUserId },\n      include: {\n        post: true,\n      },\n    });\n\n    const savedPosts = saved.map((item) => item.post);\n    res.status(200).json({ userPosts, savedPosts });\n  } catch (err) {\n    console.log(err);\n    res.status(500).json({ message: \"Failed to get profile posts!\" });\n  }\n};\n\nexport const getNotificationNumber = async (req, res) => {\n  const tokenUserId = req.userId;\n  try {\n    const number = await prisma.chat.count({\n      where: {\n        userIDs: {\n          hasSome: [tokenUserId],\n        },\n        NOT: {\n          seenBy: {\n            hasSome: [tokenUserId],\n          },\n        },\n      },\n    });\n    res.status(200).json(number);\n  } catch (err) {\n    console.log(err);\n    res.status(500).json({ message: \"Failed to get profile posts!\" });\n  }\n};\n"
  },
  {
    "path": "api/lib/prisma.js",
    "content": "import { PrismaClient } from \"@prisma/client\";\n\nconst prisma = new PrismaClient();\n\nexport default prisma;\n"
  },
  {
    "path": "api/middleware/verifyToken.js",
    "content": "import jwt from \"jsonwebtoken\";\n\nexport const verifyToken = (req, res, next) => {\n  const token = req.cookies.token;\n\n  if (!token) return res.status(401).json({ message: \"Not Authenticated!\" });\n\n  jwt.verify(token, process.env.JWT_SECRET_KEY, async (err, payload) => {\n    if (err) return res.status(403).json({ message: \"Token is not Valid!\" });\n    req.userId = payload.id;\n\n    next();\n  });\n};\n"
  },
  {
    "path": "api/package.json",
    "content": "{\n  \"name\": \"api\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"app.js\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"@prisma/client\": \"^5.11.0\",\n    \"bcrypt\": \"^5.1.1\",\n    \"cookie-parser\": \"^1.4.6\",\n    \"cors\": \"^2.8.5\",\n    \"express\": \"^4.18.3\",\n    \"jsonwebtoken\": \"^9.0.2\",\n    \"prisma\": \"^5.11.0\"\n  }\n}\n"
  },
  {
    "path": "api/prisma/schema.prisma",
    "content": "// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\n// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?\n// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init\n\ngenerator client {\n  provider = \"prisma-client-js\"\n}\n\ndatasource db {\n  provider = \"mongodb\"\n  url      = env(\"DATABASE_URL\")\n}\n\nmodel Post {\n  id         String      @id @default(auto()) @map(\"_id\") @db.ObjectId\n  title      String\n  price      Int\n  images     String[]\n  address    String\n  city       String\n  bedroom    Int\n  bathroom   Int\n  latitude   String\n  longitude  String\n  type       Type\n  property   Property\n  createdAt  DateTime    @default(now())\n  user       User        @relation(fields: [userId], references: [id])\n  userId     String      @db.ObjectId\n  postDetail PostDetail?\n  savedPosts SavedPost[]\n}\n\nenum Type {\n  buy\n  rent\n}\n\nenum Property {\n  apartment\n  house\n  condo\n  land\n}\n\nmodel PostDetail {\n  id         String  @id @default(auto()) @map(\"_id\") @db.ObjectId\n  desc       String\n  utilities  String?\n  pet        String?\n  income     String?\n  size       Int?\n  school     Int?\n  bus        Int?\n  restaurant Int?\n  post       Post    @relation(fields: [postId], references: [id])\n  postId     String  @unique @db.ObjectId\n}\n\nmodel SavedPost {\n  id        String   @id @default(auto()) @map(\"_id\") @db.ObjectId\n  user      User     @relation(fields: [userId], references: [id])\n  post      Post     @relation(fields: [postId], references: [id])\n  userId    String   @unique @db.ObjectId\n  postId    String   @unique @db.ObjectId\n  createdAt DateTime @default(now())\n\n  @@unique([userId, postId])\n}\n\nmodel User {\n  id         String      @id @default(auto()) @map(\"_id\") @db.ObjectId\n  email      String      @unique\n  username   String      @unique\n  password   String\n  avatar     String?\n  createdAt  DateTime    @default(now())\n  posts      Post[]\n  savedPosts SavedPost[]\n  chats      Chat[]      @relation(fields: [chatIDs], references: [id])\n  chatIDs    String[]    @db.ObjectId\n}\n\nmodel Chat {\n  id        String    @id @default(auto()) @map(\"_id\") @db.ObjectId\n  users     User[]    @relation(fields: [userIDs], references: [id])\n  userIDs   String[]  @db.ObjectId\n  createdAt DateTime  @default(now())\n  seenBy    String[]  @db.ObjectId\n  messages  Message[]\n  lastMessage String?\n}\n\nmodel Message {\n  id        String   @id @default(auto()) @map(\"_id\") @db.ObjectId\n  text      String\n  userId    String\n  chat      Chat     @relation(fields: [chatId], references: [id])\n  chatId    String   @db.ObjectId\n  createdAt DateTime @default(now())\n}\n"
  },
  {
    "path": "api/routes/auth.route.js",
    "content": "import express from \"express\";\nimport { login, logout, register } from \"../controllers/auth.controller.js\";\n\nconst router = express.Router();\n\nrouter.post(\"/register\", register);\nrouter.post(\"/login\", login);\nrouter.post(\"/logout\", logout);\n\nexport default router;\n"
  },
  {
    "path": "api/routes/chat.route.js",
    "content": "import express from \"express\";\nimport {\n  getChats,\n  getChat,\n  addChat,\n  readChat,\n} from \"../controllers/chat.controller.js\";\nimport { verifyToken } from \"../middleware/verifyToken.js\";\n\nconst router = express.Router();\n\nrouter.get(\"/\", verifyToken, getChats);\nrouter.get(\"/:id\", verifyToken, getChat);\nrouter.post(\"/\", verifyToken, addChat);\nrouter.put(\"/read/:id\", verifyToken, readChat);\n\nexport default router;\n"
  },
  {
    "path": "api/routes/message.route.js",
    "content": "import express from \"express\";\nimport {\n  addMessage\n} from \"../controllers/message.controller.js\";\nimport {verifyToken} from \"../middleware/verifyToken.js\";\n\nconst router = express.Router();\n\n\nrouter.post(\"/:chatId\", verifyToken, addMessage);\n\nexport default router;\n"
  },
  {
    "path": "api/routes/post.route.js",
    "content": "import express from \"express\";\nimport {verifyToken} from \"../middleware/verifyToken.js\";\nimport { addPost, deletePost, getPost, getPosts, updatePost } from \"../controllers/post.controller.js\";\n\nconst router = express.Router();\n\nrouter.get(\"/\", getPosts);\nrouter.get(\"/:id\", getPost);\nrouter.post(\"/\", verifyToken, addPost);\nrouter.put(\"/:id\", verifyToken, updatePost);\nrouter.delete(\"/:id\", verifyToken, deletePost);\n\nexport default router;\n"
  },
  {
    "path": "api/routes/test.route.js",
    "content": "import express from \"express\";\nimport { shouldBeAdmin, shouldBeLoggedIn } from \"../controllers/test.controller.js\";\nimport { verifyToken } from \"../middleware/verifyToken.js\";\n\nconst router = express.Router();\n\nrouter.get(\"/should-be-logged-in\", verifyToken, shouldBeLoggedIn);\n\nrouter.get(\"/should-be-admin\", shouldBeAdmin);\n\nexport default router;\n"
  },
  {
    "path": "api/routes/user.route.js",
    "content": "import express from \"express\";\nimport {\n  deleteUser,\n  getUser,\n  getUsers,\n  updateUser,\n  savePost,\n  profilePosts,\n  getNotificationNumber\n} from \"../controllers/user.controller.js\";\nimport {verifyToken} from \"../middleware/verifyToken.js\";\n\nconst router = express.Router();\n\nrouter.get(\"/\", getUsers);\n// router.get(\"/search/:id\", verifyToken, getUser);\nrouter.put(\"/:id\", verifyToken, updateUser);\nrouter.delete(\"/:id\", verifyToken, deleteUser);\nrouter.post(\"/save\", verifyToken, savePost);\nrouter.get(\"/profilePosts\", verifyToken, profilePosts);\nrouter.get(\"/notification\", verifyToken, getNotificationNumber);\n\nexport default router;\n"
  },
  {
    "path": "client/.eslintrc.cjs",
    "content": "module.exports = {\n  root: true,\n  env: { browser: true, es2020: true },\n  extends: [\n    'eslint:recommended',\n    'plugin:react/recommended',\n    'plugin:react/jsx-runtime',\n    'plugin:react-hooks/recommended',\n  ],\n  ignorePatterns: ['dist', '.eslintrc.cjs'],\n  parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },\n  settings: { react: { version: '18.2' } },\n  plugins: ['react-refresh'],\n  rules: {\n    \"react/prop-types\": \"off\",\n    \"no-unused-vars\": \"warn\",\n    'react/jsx-no-target-blank': 'off',\n    'react-refresh/only-export-components': [\n      'warn',\n      { allowConstantExport: true },\n    ],\n  },\n}\n"
  },
  {
    "path": "client/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "client/README.md",
    "content": "# React Real Estate UI Design"
  },
  {
    "path": "client/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/favicon.png\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Lama Real Estate App</title>\n    <meta name=\"description\" content=\"Buy, Sell and Rent Properties\" />\n    <link\n      href=\"https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap\"\n      rel=\"stylesheet\"\n    />\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.jsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "client/package.json",
    "content": "{\n  \"name\": \"estateui\",\n  \"private\": true,\n  \"version\": \"0.0.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"lint\": \"eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"axios\": \"^1.6.7\",\n    \"dompurify\": \"^3.0.9\",\n    \"leaflet\": \"^1.9.4\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"react-leaflet\": \"^4.2.1\",\n    \"react-quill\": \"^2.0.0\",\n    \"react-router-dom\": \"^6.22.2\",\n    \"sass\": \"^1.71.1\",\n    \"socket.io-client\": \"^4.7.5\",\n    \"timeago.js\": \"^4.0.2\",\n    \"zustand\": \"^4.5.2\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.2.56\",\n    \"@types/react-dom\": \"^18.2.19\",\n    \"@vitejs/plugin-react\": \"^4.2.1\",\n    \"eslint\": \"^8.56.0\",\n    \"eslint-plugin-react\": \"^7.33.2\",\n    \"eslint-plugin-react-hooks\": \"^4.6.0\",\n    \"eslint-plugin-react-refresh\": \"^0.4.5\",\n    \"vite\": \"^5.1.4\"\n  }\n}\n"
  },
  {
    "path": "client/src/App.jsx",
    "content": "import HomePage from \"./routes/homePage/homePage\";\nimport { createBrowserRouter, RouterProvider } from \"react-router-dom\";\nimport ListPage from \"./routes/listPage/listPage\";\nimport { Layout, RequireAuth } from \"./routes/layout/layout\";\nimport SinglePage from \"./routes/singlePage/singlePage\";\nimport ProfilePage from \"./routes/profilePage/profilePage\";\nimport Login from \"./routes/login/login\";\nimport Register from \"./routes/register/register\";\nimport ProfileUpdatePage from \"./routes/profileUpdatePage/profileUpdatePage\";\nimport NewPostPage from \"./routes/newPostPage/newPostPage\";\nimport { listPageLoader, profilePageLoader, singlePageLoader } from \"./lib/loaders\";\n\nfunction App() {\n  const router = createBrowserRouter([\n    {\n      path: \"/\",\n      element: <Layout />,\n      children: [\n        {\n          path: \"/\",\n          element: <HomePage />,\n        },\n        {\n          path: \"/list\",\n          element: <ListPage />,\n          loader: listPageLoader,\n        },\n        {\n          path: \"/:id\",\n          element: <SinglePage />,\n          loader: singlePageLoader,\n        },\n\n        {\n          path: \"/login\",\n          element: <Login />,\n        },\n        {\n          path: \"/register\",\n          element: <Register />,\n        },\n      ],\n    },\n    {\n      path: \"/\",\n      element: <RequireAuth />,\n      children: [\n        {\n          path: \"/profile\",\n          element: <ProfilePage />,\n          loader: profilePageLoader\n        },\n        {\n          path: \"/profile/update\",\n          element: <ProfileUpdatePage />,\n        },\n        {\n          path: \"/add\",\n          element: <NewPostPage />,\n        },\n      ],\n    },\n  ]);\n\n  return <RouterProvider router={router} />;\n}\n\nexport default App;\n"
  },
  {
    "path": "client/src/components/card/Card.jsx",
    "content": "import { Link } from \"react-router-dom\";\nimport \"./card.scss\";\n\nfunction Card({ item }) {\n  return (\n    <div className=\"card\">\n      <Link to={`/${item.id}`} className=\"imageContainer\">\n        <img src={item.images[0]} alt=\"\" />\n      </Link>\n      <div className=\"textContainer\">\n        <h2 className=\"title\">\n          <Link to={`/${item.id}`}>{item.title}</Link>\n        </h2>\n        <p className=\"address\">\n          <img src=\"/pin.png\" alt=\"\" />\n          <span>{item.address}</span>\n        </p>\n        <p className=\"price\">$ {item.price}</p>\n        <div className=\"bottom\">\n          <div className=\"features\">\n            <div className=\"feature\">\n              <img src=\"/bed.png\" alt=\"\" />\n              <span>{item.bedroom} bedroom</span>\n            </div>\n            <div className=\"feature\">\n              <img src=\"/bath.png\" alt=\"\" />\n              <span>{item.bathroom} bathroom</span>\n            </div>\n          </div>\n          <div className=\"icons\">\n            <div className=\"icon\">\n              <img src=\"/save.png\" alt=\"\" />\n            </div>\n            <div className=\"icon\">\n              <img src=\"/chat.png\" alt=\"\" />\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default Card;\n"
  },
  {
    "path": "client/src/components/card/card.scss",
    "content": "@import \"../../responsive.scss\";\n\n.card {\n  display: flex;\n  gap: 20px;\n\n  .imageContainer {\n    flex: 2;\n    height: 200px;\n\n    @include md {\n      display: none;\n    }\n\n    img {\n      width: 100%;\n      height: 100%;\n      object-fit: cover;\n      border-radius: 10px;\n    }\n  }\n  .textContainer {\n    flex: 3;\n    display: flex;\n    flex-direction: column;\n    justify-content: space-between;\n    gap: 10px;\n\n    img {\n      width: 16px;\n      height: 16px;\n    }\n\n    .title {\n      font-size: 20px;\n      font-weight: 600;\n      color: #444;\n      transition: all 0.4s ease;\n\n      &:hover {\n        color: #000;\n        scale: 1.01;\n      }\n    }\n\n    .address {\n      font-size: 14px;\n      display: flex;\n      align-items: center;\n      gap: 5px;\n      color: #888;\n    }\n\n    .price {\n      font-size: 20px;\n      font-weight: 300;\n      padding: 5px;\n      border-radius: 5px;\n      background-color: rgba(254, 205, 81, 0.438);\n      width: max-content;\n    }\n\n    .bottom {\n      display: flex;\n      justify-content: space-between;\n      gap: 10px;\n\n      .features {\n        display: flex;\n        gap: 20px;\n        font-size: 14px;\n\n        .feature {\n          display: flex;\n          align-items: center;\n          gap: 5px;\n          background-color: whitesmoke;\n          padding: 5px;\n          border-radius: 5px;\n        }\n      }\n\n      .icons {\n        display: flex;\n        gap: 20px;\n\n        .icon {\n          border: 1px solid #999;\n          padding: 2px 5px;\n          border-radius: 5px;\n          cursor: pointer;\n          display: flex;\n          align-items: center;\n          justify-content: center;\n\n          &:hover {\n            background-color: lightgray;\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "client/src/components/chat/Chat.jsx",
    "content": "import { useContext, useEffect, useRef, useState } from \"react\";\nimport \"./chat.scss\";\nimport { AuthContext } from \"../../context/AuthContext\";\nimport apiRequest from \"../../lib/apiRequest\";\nimport { format } from \"timeago.js\";\nimport { SocketContext } from \"../../context/SocketContext\";\nimport { useNotificationStore } from \"../../lib/notificationStore\";\n\nfunction Chat({ chats }) {\n  const [chat, setChat] = useState(null);\n  const { currentUser } = useContext(AuthContext);\n  const { socket } = useContext(SocketContext);\n\n  const messageEndRef = useRef();\n\n  const decrease = useNotificationStore((state) => state.decrease);\n\n  useEffect(() => {\n    messageEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\n  }, [chat]);\n\n  const handleOpenChat = async (id, receiver) => {\n    try {\n      const res = await apiRequest(\"/chats/\" + id);\n      if (!res.data.seenBy.includes(currentUser.id)) {\n        decrease();\n      }\n      setChat({ ...res.data, receiver });\n    } catch (err) {\n      console.log(err);\n    }\n  };\n\n  const handleSubmit = async (e) => {\n    e.preventDefault();\n\n    const formData = new FormData(e.target);\n    const text = formData.get(\"text\");\n\n    if (!text) return;\n    try {\n      const res = await apiRequest.post(\"/messages/\" + chat.id, { text });\n      setChat((prev) => ({ ...prev, messages: [...prev.messages, res.data] }));\n      e.target.reset();\n      socket.emit(\"sendMessage\", {\n        receiverId: chat.receiver.id,\n        data: res.data,\n      });\n    } catch (err) {\n      console.log(err);\n    }\n  };\n\n  useEffect(() => {\n    const read = async () => {\n      try {\n        await apiRequest.put(\"/chats/read/\" + chat.id);\n      } catch (err) {\n        console.log(err);\n      }\n    };\n\n    if (chat && socket) {\n      socket.on(\"getMessage\", (data) => {\n        if (chat.id === data.chatId) {\n          setChat((prev) => ({ ...prev, messages: [...prev.messages, data] }));\n          read();\n        }\n      });\n    }\n    return () => {\n      socket.off(\"getMessage\");\n    };\n  }, [socket, chat]);\n\n  return (\n    <div className=\"chat\">\n      <div className=\"messages\">\n        <h1>Messages</h1>\n        {chats?.map((c) => (\n          <div\n            className=\"message\"\n            key={c.id}\n            style={{\n              backgroundColor:\n                c.seenBy.includes(currentUser.id) || chat?.id === c.id\n                  ? \"white\"\n                  : \"#fecd514e\",\n            }}\n            onClick={() => handleOpenChat(c.id, c.receiver)}\n          >\n            <img src={c.receiver.avatar || \"/noavatar.jpg\"} alt=\"\" />\n            <span>{c.receiver.username}</span>\n            <p>{c.lastMessage}</p>\n          </div>\n        ))}\n      </div>\n      {chat && (\n        <div className=\"chatBox\">\n          <div className=\"top\">\n            <div className=\"user\">\n              <img src={chat.receiver.avatar || \"noavatar.jpg\"} alt=\"\" />\n              {chat.receiver.username}\n            </div>\n            <span className=\"close\" onClick={() => setChat(null)}>\n              X\n            </span>\n          </div>\n          <div className=\"center\">\n            {chat.messages.map((message) => (\n              <div\n                className=\"chatMessage\"\n                style={{\n                  alignSelf:\n                    message.userId === currentUser.id\n                      ? \"flex-end\"\n                      : \"flex-start\",\n                  textAlign:\n                    message.userId === currentUser.id ? \"right\" : \"left\",\n                }}\n                key={message.id}\n              >\n                <p>{message.text}</p>\n                <span>{format(message.createdAt)}</span>\n              </div>\n            ))}\n            <div ref={messageEndRef}></div>\n          </div>\n          <form onSubmit={handleSubmit} className=\"bottom\">\n            <textarea name=\"text\"></textarea>\n            <button>Send</button>\n          </form>\n        </div>\n      )}\n    </div>\n  );\n}\n\nexport default Chat;\n"
  },
  {
    "path": "client/src/components/chat/chat.scss",
    "content": ".chat {\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n\n  .messages {\n    flex: 1;\n    display: flex;\n    flex-direction: column;\n    gap: 20px;\n    overflow-y: scroll;\n\n    h1 {\n      font-weight: 300;\n    }\n\n    .message {\n      background-color: white;\n      padding: 20px;\n      border-radius: 10px;\n      display: flex;\n      align-items: center;\n      gap: 20px;\n      cursor: pointer;\n\n      img {\n        width: 40px;\n        height: 40px;\n        border-radius: 50%;\n        object-fit: cover;\n      }\n\n      span {\n        font-weight: bold;\n      }\n    }\n  }\n\n  .chatBox {\n    flex: 1;\n    background-color: white;\n    display: flex;\n    flex-direction: column;\n    justify-content: space-between;\n\n    .top {\n      background-color: #f7c14b85;\n      padding: 20px;\n      font-weight: bold;\n      display: flex;\n      align-items: center;\n      justify-content: space-between;\n\n      .user {\n        display: flex;\n        align-items: center;\n        gap: 20px;\n\n        img {\n          width: 30px;\n          height: 30px;\n          border-radius: 50%;\n          object-fit: cover;\n        }\n      }\n      .close {\n        cursor: pointer;\n      }\n    }\n    .center {\n      height: 350px;\n      overflow: scroll;\n      padding: 20px;\n      display: flex;\n      flex-direction: column;\n      gap: 20px;\n\n      .chatMessage{\n        width: 50%;\n\n        &.own{\n          align-self: flex-end;\n          text-align: right;\n        }\n\n        span{\n          font-size: 12px;\n          background-color: #f7c14b39;\n          padding: 2px;\n          border-radius: 5px;\n        }\n      }\n    }\n    .bottom {\n      border-top: 2px solid #f7c14b85;\n      height: 60px;\n      display: flex;\n      align-items: center;\n      justify-content: space-between;\n\n      textarea{\n        flex:3;\n        height: 100%;\n        border: none;\n        padding: 20px;\n      }\n\n      button{\n        flex:1;\n        background-color: #f7c14b85;\n        height: 100%;\n        border: none;\n        cursor: pointer;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "client/src/components/filter/Filter.jsx",
    "content": "import { useState } from \"react\";\nimport \"./filter.scss\";\nimport { useSearchParams } from \"react-router-dom\";\n\nfunction Filter() {\n  const [searchParams, setSearchParams] = useSearchParams();\n  const [query, setQuery] = useState({\n    type: searchParams.get(\"type\") || \"\",\n    city: searchParams.get(\"city\") || \"\",\n    property: searchParams.get(\"property\") || \"\",\n    minPrice: searchParams.get(\"minPrice\") || \"\",\n    maxPrice: searchParams.get(\"maxPrice\") || \"\",\n    bedroom: searchParams.get(\"bedroom\") || \"\",\n  });\n\n  const handleChange = (e) => {\n    setQuery({\n      ...query,\n      [e.target.name]: e.target.value,\n    });\n  };\n\n  const handleFilter = () => {\n    setSearchParams(query);\n  };\n\n  return (\n    <div className=\"filter\">\n      <h1>\n        Search results for <b>{searchParams.get(\"city\")}</b>\n      </h1>\n      <div className=\"top\">\n        <div className=\"item\">\n          <label htmlFor=\"city\">Location</label>\n          <input\n            type=\"text\"\n            id=\"city\"\n            name=\"city\"\n            placeholder=\"City Location\"\n            onChange={handleChange}\n            defaultValue={query.city}\n          />\n        </div>\n      </div>\n      <div className=\"bottom\">\n        <div className=\"item\">\n          <label htmlFor=\"type\">Type</label>\n          <select\n            name=\"type\"\n            id=\"type\"\n            onChange={handleChange}\n            defaultValue={query.type}\n          >\n            <option value=\"\">any</option>\n            <option value=\"buy\">Buy</option>\n            <option value=\"rent\">Rent</option>\n          </select>\n        </div>\n        <div className=\"item\">\n          <label htmlFor=\"property\">Property</label>\n          <select\n            name=\"property\"\n            id=\"property\"\n            onChange={handleChange}\n            defaultValue={query.property}\n          >\n            <option value=\"\">any</option>\n            <option value=\"apartment\">Apartment</option>\n            <option value=\"house\">House</option>\n            <option value=\"condo\">Condo</option>\n            <option value=\"land\">Land</option>\n          </select>\n        </div>\n        <div className=\"item\">\n          <label htmlFor=\"minPrice\">Min Price</label>\n          <input\n            type=\"number\"\n            id=\"minPrice\"\n            name=\"minPrice\"\n            placeholder=\"any\"\n            onChange={handleChange}\n            defaultValue={query.minPrice}\n          />\n        </div>\n        <div className=\"item\">\n          <label htmlFor=\"maxPrice\">Max Price</label>\n          <input\n            type=\"text\"\n            id=\"maxPrice\"\n            name=\"maxPrice\"\n            placeholder=\"any\"\n            onChange={handleChange}\n            defaultValue={query.maxPrice}\n          />\n        </div>\n        <div className=\"item\">\n          <label htmlFor=\"bedroom\">Bedroom</label>\n          <input\n            type=\"text\"\n            id=\"bedroom\"\n            name=\"bedroom\"\n            placeholder=\"any\"\n            onChange={handleChange}\n            defaultValue={query.bedroom}\n          />\n        </div>\n        <button onClick={handleFilter}>\n          <img src=\"/search.png\" alt=\"\" />\n        </button>\n      </div>\n    </div>\n  );\n}\n\nexport default Filter;\n"
  },
  {
    "path": "client/src/components/filter/filter.scss",
    "content": ".filter {\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n\n  h1 {\n    font-weight: 300;\n    font-size: 24px;\n  }\n\n  .item {\n    display: flex;\n    flex-direction: column;\n    gap: 2px;\n\n    label {\n      font-size: 10px;\n    }\n\n    input,\n    select {\n      width: 100px;\n      padding: 10px;\n      border: 1px solid #e0e0e0;\n      border-radius: 5px;\n      font-size: 14px;\n    }\n  }\n\n  .top {\n    input {\n      width: 100%;\n    }\n  }\n\n  .bottom {\n    display: flex;\n    justify-content: space-between;\n    flex-wrap: wrap;\n    gap: 20px;\n\n    button {\n      width: 100px;\n      padding: 10px;\n      border: none;\n      cursor: pointer;\n      background-color: #fece51;\n\n      img {\n        width: 24px;\n        height: 24px;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "client/src/components/list/List.jsx",
    "content": "import './list.scss'\nimport Card from\"../card/Card\"\n\nfunction List({posts}){\n  return (\n    <div className='list'>\n      {posts.map(item=>(\n        <Card key={item.id} item={item}/>\n      ))}\n    </div>\n  )\n}\n\nexport default List"
  },
  {
    "path": "client/src/components/list/list.scss",
    "content": ".list{\n  display: flex;\n  flex-direction: column;\n  gap: 50px;\n}"
  },
  {
    "path": "client/src/components/map/Map.jsx",
    "content": "import { MapContainer, TileLayer } from \"react-leaflet\";\nimport \"./map.scss\";\nimport \"leaflet/dist/leaflet.css\";\nimport Pin from \"../pin/Pin\";\n\nfunction Map({ items }) {\n  return (\n    <MapContainer\n      center={\n        items.length === 1\n          ? [items[0].latitude, items[0].longitude]\n          : [52.4797, -1.90269]\n      }\n      zoom={7}\n      scrollWheelZoom={false}\n      className=\"map\"\n    >\n      <TileLayer\n        attribution='&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors'\n        url=\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\"\n      />\n      {items.map((item) => (\n        <Pin item={item} key={item.id} />\n      ))}\n    </MapContainer>\n  );\n}\n\nexport default Map;\n"
  },
  {
    "path": "client/src/components/map/map.scss",
    "content": ".map{\n  width: 100%;\n  height: 100%;\n  border-radius: 20px;\n}"
  },
  {
    "path": "client/src/components/navbar/Navbar.jsx",
    "content": "import { useContext, useState } from \"react\";\nimport \"./navbar.scss\";\nimport { Link } from \"react-router-dom\";\nimport { AuthContext } from \"../../context/AuthContext\";\nimport { useNotificationStore } from \"../../lib/notificationStore\";\n\nfunction Navbar() {\n  const [open, setOpen] = useState(false);\n\n  const { currentUser } = useContext(AuthContext);\n\n  const fetch = useNotificationStore((state) => state.fetch);\n  const number = useNotificationStore((state) => state.number);\n\n  if(currentUser) fetch();\n\n  return (\n    <nav>\n      <div className=\"left\">\n        <a href=\"/\" className=\"logo\">\n          <img src=\"/logo.png\" alt=\"\" />\n          <span>LamaEstate</span>\n        </a>\n        <a href=\"/\">Home</a>\n        <a href=\"/\">About</a>\n        <a href=\"/\">Contact</a>\n        <a href=\"/\">Agents</a>\n      </div>\n      <div className=\"right\">\n        {currentUser ? (\n          <div className=\"user\">\n            <img src={currentUser.avatar || \"/noavatar.jpg\"} alt=\"\" />\n            <span>{currentUser.username}</span>\n            <Link to=\"/profile\" className=\"profile\">\n              {number > 0 && <div className=\"notification\">{number}</div>}\n              <span>Profile</span>\n            </Link>\n          </div>\n        ) : (\n          <>\n            <a href=\"/login\">Sign in</a>\n            <a href=\"/register\" className=\"register\">\n              Sign up\n            </a>\n          </>\n        )}\n        <div className=\"menuIcon\">\n          <img\n            src=\"/menu.png\"\n            alt=\"\"\n            onClick={() => setOpen((prev) => !prev)}\n          />\n        </div>\n        <div className={open ? \"menu active\" : \"menu\"}>\n          <a href=\"/\">Home</a>\n          <a href=\"/\">About</a>\n          <a href=\"/\">Contact</a>\n          <a href=\"/\">Agents</a>\n          <a href=\"/\">Sign in</a>\n          <a href=\"/\">Sign up</a>\n        </div>\n      </div>\n    </nav>\n  );\n}\n\nexport default Navbar;\n"
  },
  {
    "path": "client/src/components/navbar/navbar.scss",
    "content": "@import \"../../responsive.scss\";\n\nnav {\n  height: 100px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n\n  a {\n    transition: all 0.4s ease;\n\n    @include sm {\n      display: none;\n    }\n\n    &:hover {\n      scale: 1.05;\n    }\n  }\n\n  .left {\n    flex: 3;\n    display: flex;\n    align-items: center;\n    gap: 50px;\n\n    .logo {\n      font-weight: bold;\n      font-size: 20px;\n      display: flex;\n      align-items: center;\n      gap: 10px;\n\n      img {\n        width: 28px;\n      }\n\n      span {\n        @include md {\n          display: none;\n        }\n\n        @include sm {\n          display: initial;\n        }\n      }\n    }\n  }\n  .right {\n    flex: 2;\n    display: flex;\n    align-items: center;\n    justify-content: flex-end;\n    background-color: #fcf5f3;\n    height: 100%;\n\n    @include md {\n      background-color: transparent;\n    }\n\n    a {\n      padding: 12px 24px;\n      margin: 20px;\n    }\n\n    .user{\n      display: flex;\n      align-items: center;\n      font-weight: bold;\n\n      img{\n        width: 40px;\n        height: 40px;\n        border-radius: 50%;\n        object-fit: cover;\n        margin-right: 20px;\n      }\n\n      span{\n        @include sm{\n          display: none;\n        }\n      }\n\n      .profile{\n        padding: 12px 24px;\n        background-color: #fece51;\n        cursor: pointer;\n        border: none;\n        position: relative;\n        \n        .notification{\n          position: absolute;\n          top: -8px;\n          right: -8px;\n          background-color: red;\n          color: white;\n          border-radius: 50%;\n          width: 26px;\n          height: 26px;\n          display: flex;\n          align-items: center;\n          justify-content: center;\n        }\n\n      }\n    }\n\n    .register {\n      background-color: #fece51;\n    }\n\n    .menuIcon {\n      display: none;\n      z-index: 999;\n\n      img {\n        width: 36px;\n        height: 36px;\n        cursor: pointer;\n      }\n\n      @include sm {\n        display: inline;\n      }\n    }\n\n    .menu {\n      position: absolute;\n      top: 0;\n      right: -50%;\n      background-color: black;\n      color: white;\n      height: 100vh;\n      width: 50%;\n      transition: all 1s ease;\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n      justify-content: center;\n      font-size: 24px;\n\n      &.active {\n        right: 0;\n      }\n\n      @include sm {\n        a {\n          display: initial;\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "client/src/components/pin/Pin.jsx",
    "content": "import { Marker, Popup } from \"react-leaflet\";\nimport \"./pin.scss\";\nimport { Link } from \"react-router-dom\";\n\nfunction Pin({ item }) {\n  return (\n    <Marker position={[item.latitude, item.longitude]}>\n      <Popup>\n        <div className=\"popupContainer\">\n          <img src={item.images[0]} alt=\"\" />\n          <div className=\"textContainer\">\n            <Link to={`/${item.id}`}>{item.title}</Link>\n            <span>{item.bedroom} bedroom</span>\n            <b>$ {item.price}</b>\n          </div>\n        </div>\n      </Popup>\n    </Marker>\n  );\n}\n\nexport default Pin;\n"
  },
  {
    "path": "client/src/components/pin/pin.scss",
    "content": ".popupContainer{\n  display: flex;\n  gap: 20px;\n\n  img{\n    width: 64px;\n    height: 48px;\n    object-fit: cover;\n    border-radius: 5px;\n  }\n\n  .textContainer{\n    display: flex;\n    flex-direction: column;\n    justify-content: space-between;\n  }\n}"
  },
  {
    "path": "client/src/components/searchBar/SearchBar.jsx",
    "content": "import { useState } from \"react\";\nimport \"./searchBar.scss\";\nimport { Link } from \"react-router-dom\";\n\nconst types = [\"buy\", \"rent\"];\n\nfunction SearchBar() {\n  const [query, setQuery] = useState({\n    type: \"buy\",\n    city: \"\",\n    minPrice: 0,\n    maxPrice: 0,\n  });\n\n  const switchType = (val) => {\n    setQuery((prev) => ({ ...prev, type: val }));\n  };\n\n  const handleChange = (e) => {\n    setQuery((prev) => ({ ...prev, [e.target.name]: e.target.value }));\n  };\n\n  return (\n    <div className=\"searchBar\">\n      <div className=\"type\">\n        {types.map((type) => (\n          <button\n            key={type}\n            onClick={() => switchType(type)}\n            className={query.type === type ? \"active\" : \"\"}\n          >\n            {type}\n          </button>\n        ))}\n      </div>\n      <form>\n        <input\n          type=\"text\"\n          name=\"city\"\n          placeholder=\"City\"\n          onChange={handleChange}\n        />\n        <input\n          type=\"number\"\n          name=\"minPrice\"\n          min={0}\n          max={10000000}\n          placeholder=\"Min Price\"\n          onChange={handleChange}\n        />\n        <input\n          type=\"number\"\n          name=\"maxPrice\"\n          min={0}\n          max={10000000}\n          placeholder=\"Max Price\"\n          onChange={handleChange}\n        />\n        <Link\n          to={`/list?type=${query.type}&city=${query.city}&minPrice=${query.minPrice}&maxPrice=${query.maxPrice}`}\n        >\n          <button>\n            <img src=\"/search.png\" alt=\"\" />\n          </button>\n        </Link>\n      </form>\n    </div>\n  );\n}\n\nexport default SearchBar;\n"
  },
  {
    "path": "client/src/components/searchBar/searchBar.scss",
    "content": "@import \"../../responsive.scss\";\n\n.searchBar {\n  .type {\n\n\n    button {\n      padding: 16px 36px;\n      border: 1px solid #999;\n      border-bottom: none;\n      cursor: pointer;\n      background-color: white;\n      text-transform: capitalize;\n\n      &.active {\n        background-color: black;\n        color: white;\n      }\n\n      &:first-child {\n        border-top-left-radius: 5px;\n        border-right: none;\n      }\n      &:last-child {\n        border-top-right-radius: 5px;\n        border-left: none;\n      }\n    }\n  }\n\n  form {\n    border: 1px solid #999;\n    display: flex;\n    justify-content: space-between;\n    height: 64px;\n    gap: 5px;\n\n    @include sm {\n      flex-direction: column;\n      border: none;\n    }\n\n    input {\n      border: none;\n      padding: 0px 10px;\n      width: 200px;\n\n      @include lg {\n        padding: 0px 5px;\n\n        &:nth-child(2),\n        &:nth-child(3) {\n          width: 140px;\n        }\n      }\n      @include md {\n        width: 200px;\n        &:nth-child(2),\n        &:nth-child(3) {\n          width: 200px;\n        }\n      }\n\n      @include sm{\n        width: auto;\n        &:nth-child(2),\n        &:nth-child(3) {\n          width: auto;\n        }\n        padding: 20px;\n        border: 1px solid #999;\n      }\n    }\n\n    a{\n      flex: 1;\n      background-color: #fece51;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n\n      button {\n        border: none;\n        cursor: pointer;\n        background-color: #fece51;\n        \n        @include sm{\n          padding: 10px;\n        }\n        \n        img {\n          width: 24px;\n          height: 24px;\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "client/src/components/slider/Slider.jsx",
    "content": "import { useState } from \"react\";\nimport \"./slider.scss\";\n\nfunction Slider({ images }) {\n  const [imageIndex, setImageIndex] = useState(null);\n\n  const changeSlide = (direction) => {\n    if (direction === \"left\") {\n      if (imageIndex === 0) {\n        setImageIndex(images.length - 1);\n      } else {\n        setImageIndex(imageIndex - 1);\n      }\n    } else {\n      if (imageIndex === images.length - 1) {\n        setImageIndex(0);\n      } else {\n        setImageIndex(imageIndex + 1);\n      }\n    }\n  };\n\n  return (\n    <div className=\"slider\">\n      {imageIndex !== null && (\n        <div className=\"fullSlider\">\n          <div className=\"arrow\" onClick={() => changeSlide(\"left\")}>\n            <img src=\"/arrow.png\" alt=\"\" />\n          </div>\n          <div className=\"imgContainer\">\n            <img src={images[imageIndex]} alt=\"\" />\n          </div>\n          <div className=\"arrow\" onClick={() => changeSlide(\"right\")}>\n            <img src=\"/arrow.png\" className=\"right\" alt=\"\" />\n          </div>\n          <div className=\"close\" onClick={() => setImageIndex(null)}>\n            X\n          </div>\n        </div>\n      )}\n      <div className=\"bigImage\">\n        <img src={images[0]} alt=\"\" onClick={() => setImageIndex(0)} />\n      </div>\n      <div className=\"smallImages\">\n        {images.slice(1).map((image, index) => (\n          <img\n            src={image}\n            alt=\"\"\n            key={index}\n            onClick={() => setImageIndex(index + 1)}\n          />\n        ))}\n      </div>\n    </div>\n  );\n}\n\nexport default Slider;\n"
  },
  {
    "path": "client/src/components/slider/slider.scss",
    "content": "@import \"../../responsive.scss\";\n\n.slider {\n  width: 100%;\n  height: 350px;\n  display: flex;\n  gap: 20px;\n\n  @include sm {\n    height: 280px;\n  }\n\n  .fullSlider {\n    position: absolute;\n    width: 100vw;\n    height: 100vh;\n    top: 0;\n    left: 0;\n    background-color: black;\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    z-index: 9999;\n\n    .arrow {\n      flex: 1;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n\n      img {\n        width: 50px;\n\n        @include md {\n          width: 30px;\n        }\n\n        @include sm {\n          width: 20px;\n        }\n\n        &.right {\n          transform: rotate(180deg);\n        }\n      }\n    }\n    .imgContainer {\n      flex: 10;\n\n      img {\n        width: 100%;\n        height: 100%;\n        object-fit: cover;\n      }\n    }\n\n    .close {\n      position: absolute;\n      top: 0;\n      right: 0;\n      color: white;\n      font-size: 36px;\n      font-weight: bold;\n      padding: 50px;\n      cursor: pointer;\n    }\n  }\n\n  img {\n    width: 100%;\n    height: 100%;\n    object-fit: cover;\n    border-radius: 10px;\n    cursor: pointer;\n  }\n\n  .bigImage {\n    flex: 3;\n\n    @include sm {\n      flex: 2;\n    }\n  }\n  .smallImages {\n    flex: 1;\n    display: flex;\n    flex-direction: column;\n    justify-content: space-between;\n    gap: 20px;\n\n    @include sm {\n      flex: 1;\n    }\n\n    img {\n      height: 100px;\n\n      @include sm {\n        height: 80px;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "client/src/components/uploadWidget/UploadWidget.jsx",
    "content": "import { createContext, useEffect, useState } from \"react\";\n\n// Create a context to manage the script loading state\nconst CloudinaryScriptContext = createContext();\n\nfunction UploadWidget({ uwConfig, setPublicId, setState }) {\n  const [loaded, setLoaded] = useState(false);\n\n  useEffect(() => {\n    // Check if the script is already loaded\n    if (!loaded) {\n      const uwScript = document.getElementById(\"uw\");\n      if (!uwScript) {\n        // If not loaded, create and load the script\n        const script = document.createElement(\"script\");\n        script.setAttribute(\"async\", \"\");\n        script.setAttribute(\"id\", \"uw\");\n        script.src = \"https://upload-widget.cloudinary.com/global/all.js\";\n        script.addEventListener(\"load\", () => setLoaded(true));\n        document.body.appendChild(script);\n      } else {\n        // If already loaded, update the state\n        setLoaded(true);\n      }\n    }\n  }, [loaded]);\n\n  const initializeCloudinaryWidget = () => {\n    if (loaded) {\n      var myWidget = window.cloudinary.createUploadWidget(\n        uwConfig,\n        (error, result) => {\n          if (!error && result && result.event === \"success\") {\n            console.log(\"Done! Here is the image info: \", result.info);\n            setState((prev) => [...prev, result.info.secure_url]);\n          }\n        }\n      );\n\n      document.getElementById(\"upload_widget\").addEventListener(\n        \"click\",\n        function () {\n          myWidget.open();\n        },\n        false\n      );\n    }\n  };\n\n  return (\n    <CloudinaryScriptContext.Provider value={{ loaded }}>\n      <button\n        id=\"upload_widget\"\n        className=\"cloudinary-button\"\n        onClick={initializeCloudinaryWidget}\n      >\n        Upload\n      </button>\n    </CloudinaryScriptContext.Provider>\n  );\n}\n\nexport default UploadWidget;\nexport { CloudinaryScriptContext };\n"
  },
  {
    "path": "client/src/context/AuthContext.jsx",
    "content": "import { createContext, useEffect, useState } from \"react\";\n\nexport const AuthContext = createContext();\n\nexport const AuthContextProvider = ({ children }) => {\n  const [currentUser, setCurrentUser] = useState(\n    JSON.parse(localStorage.getItem(\"user\")) || null\n  );\n\n  const updateUser = (data) => {\n    setCurrentUser(data);\n  };\n\n  useEffect(() => {\n    localStorage.setItem(\"user\", JSON.stringify(currentUser));\n  }, [currentUser]);\n\n  return (\n    <AuthContext.Provider value={{ currentUser,updateUser }}>\n      {children}\n    </AuthContext.Provider>\n  );\n};\n"
  },
  {
    "path": "client/src/context/SocketContext.jsx",
    "content": "import { createContext, useContext, useEffect, useState } from \"react\";\nimport { io } from \"socket.io-client\";\nimport { AuthContext } from \"./AuthContext\";\n\nexport const SocketContext = createContext();\n\nexport const SocketContextProvider = ({ children }) => {\n  const { currentUser } = useContext(AuthContext);\n  const [socket, setSocket] = useState(null);\n\n  useEffect(() => {\n    setSocket(io(\"http://localhost:4000\"));\n  }, []);\n\n  useEffect(() => {\n  currentUser && socket?.emit(\"newUser\", currentUser.id);\n  }, [currentUser, socket]);\n\n  return (\n    <SocketContext.Provider value={{ socket }}>\n      {children}\n    </SocketContext.Provider>\n  );\n};\n"
  },
  {
    "path": "client/src/index.css",
    "content": ""
  },
  {
    "path": "client/src/index.scss",
    "content": "*{\n  padding: 0;\n  margin: 0;\n  box-sizing: border-box;\n}\n\na {\n  text-decoration: none;\n  color: inherit;\n}\n\nbody{\n  font-family: \"Lato\", sans-serif;\n  overflow: hidden;\n}"
  },
  {
    "path": "client/src/lib/apiRequest.js",
    "content": "import axios from \"axios\";\n\nconst apiRequest = axios.create({\n  baseURL: \"http://localhost:8800/api\",\n  withCredentials: true,\n});\n\nexport default apiRequest;"
  },
  {
    "path": "client/src/lib/dummydata.js",
    "content": "export const listData = [\n  {\n    id: 1,\n    title: \"A Great Apartment Next to the Beach!\",\n    images: [\"https://images.pexels.com/photos/1918291/pexels-photo-1918291.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2\"],\n    bedroom: 2,\n    bathroom: 1,\n    price: 1000,\n    address: \"456 Park Avenue, London\",\n    latitude: 51.5074,\n    longitude: -0.1278,\n  },\n  {\n    id: 2,\n    title: \"An Awesome Apartment Near the Park! Almost too good to be true!\",\n    images: [\"https://images.pexels.com/photos/1428348/pexels-photo-1428348.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2\"],\n    bedroom: 3,\n    bathroom: 2,\n    price: 1500,\n    address: \"789 Oxford Street, London\",\n    latitude: 52.4862,\n    longitude: -1.8904,\n  },\n  {\n    id: 3,\n    title: \"A New Apartment in the City!\",\n    images: [\"https://images.pexels.com/photos/2062426/pexels-photo-2062426.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2\"],\n    bedroom: 1,\n    bathroom: 1,\n    price: 800,\n    address: \"101 Baker Street, London\",\n    latitude: 53.4808,\n    longitude: -2.2426,\n  },\n  {\n    id: 4,\n    title: \"Great Location! Great Price! Great Apartment!\",\n    images: [\"https://images.pexels.com/photos/2467285/pexels-photo-2467285.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2\"],\n    bedroom: 2,\n    bathroom: 1,\n    price: 1000,\n    address: \"234 Kingsway, London,\",\n    latitude: 53.8008,\n    longitude: -1.5491,\n  },\n  {\n    id: 5,\n    title: \"Apartment 5\",\n    images: [\"https://images.pexels.com/photos/276625/pexels-photo-276625.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2\"],\n    bedroom: 3,\n    bathroom: 2,\n    price: 1500,\n    address: \"567 Victoria Road, London\",\n    latitude: 53.4084,\n    longitude: -2.9916,\n  },\n  {\n    id: 6,\n    title: \"Apartment 6\",\n    images: [\"https://images.pexels.com/photos/271816/pexels-photo-271816.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2\"],\n    bedroom: 1,\n    bathroom: 1,\n    price: 800,\n    address: \"890 Regent Street, London\",\n    latitude: 54.9783,\n    longitude: -1.6174,\n  },\n  {\n    id: 7,\n    title: \"Apartment 7\",\n    images: [\"https://images.pexels.com/photos/2029667/pexels-photo-2029667.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2\"],\n    bedroom: 2,\n    bathroom: 1,\n    price: 1000,\n    address: \"112 Piccadilly, London\",\n    latitude: 53.3811,\n    longitude: -1.4701,\n  },\n  {\n    id: 8,\n    title: \"Apartment 8\",\n    images: [\"https://images.pexels.com/photos/276724/pexels-photo-276724.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2\"],\n    bedroom: 3,\n    bathroom: 2,\n    price: 1500,\n    address: \"8765 Main High Street, London\",\n    latitude: 51.4545,\n    longitude: -2.5879,\n  },\n];\n\nexport const singlePostData = {\n  id: 1,\n  title: \"Beautiful Apartment\",\n  price: 1200,\n  images: [\n    \"https://images.pexels.com/photos/1918291/pexels-photo-1918291.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2\",\n    \"https://images.pexels.com/photos/1428348/pexels-photo-1428348.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2\",\n    \"https://images.pexels.com/photos/2062426/pexels-photo-2062426.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2\",\n    \"https://images.pexels.com/photos/2467285/pexels-photo-2467285.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2\",\n  ],\n  bedRooms: 2,\n  bathroom: 1,\n  size: 861,\n  latitude: 51.5074,\n  longitude: -0.1278,\n  city: \"London\",\n  address: \"1234 Broadway St\",\n  school: \"250m away\",\n  bus: \"100m away\",\n  restaurant: \"50m away\",\n  description:\n    \"Future alike hill pull picture swim magic chain seed engineer nest outer raise bound easy poetry gain loud weigh me recognize farmer bare danger. actually put square leg vessels earth engine matter key cup indeed body film century shut place environment were stage vertical roof bottom lady function breeze darkness beside tin view local breathe carbon swam declared magnet escape has from pile apart route coffee storm someone hold space use ahead sheep jungle closely natural attached part top grain your grade trade corn salmon trouble new bend most teacher range anybody every seat fifteen eventually\",\n};\n\nexport const userData = {\n  id: 1,\n  name: \"John Doe\",\n  img: \"https://images.pexels.com/photos/91227/pexels-photo-91227.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2\",\n};"
  },
  {
    "path": "client/src/lib/loaders.js",
    "content": "import { defer } from \"react-router-dom\";\nimport apiRequest from \"./apiRequest\";\n\nexport const singlePageLoader = async ({ request, params }) => {\n  const res = await apiRequest(\"/posts/\" + params.id);\n  return res.data;\n};\nexport const listPageLoader = async ({ request, params }) => {\n  const query = request.url.split(\"?\")[1];\n  const postPromise = apiRequest(\"/posts?\" + query);\n  return defer({\n    postResponse: postPromise,\n  });\n};\n\nexport const profilePageLoader = async () => {\n  const postPromise = apiRequest(\"/users/profilePosts\");\n  const chatPromise = apiRequest(\"/chats\");\n  return defer({\n    postResponse: postPromise,\n    chatResponse: chatPromise,\n  });\n};\n"
  },
  {
    "path": "client/src/lib/notificationStore.js",
    "content": "import { create } from \"zustand\";\nimport apiRequest from \"./apiRequest\";\n\nexport const useNotificationStore = create((set) => ({\n  number: 0,\n  fetch: async () => {\n    const res = await apiRequest(\"/users/notification\");\n    set({ number: res.data });\n  },\n  decrease: () => {\n    set((prev) => ({ number: prev.number - 1 }));\n  },\n  reset: () => {\n    set({ number: 0 });\n  },\n}));\n"
  },
  {
    "path": "client/src/main.jsx",
    "content": "import React from \"react\";\nimport ReactDOM from \"react-dom/client\";\nimport App from \"./App.jsx\";\nimport \"./index.scss\";\nimport { AuthContextProvider } from \"./context/AuthContext.jsx\";\nimport { SocketContextProvider } from \"./context/SocketContext.jsx\";\n\nReactDOM.createRoot(document.getElementById(\"root\")).render(\n  <React.StrictMode>\n    <AuthContextProvider>\n      <SocketContextProvider>\n        <App />\n      </SocketContextProvider>\n    </AuthContextProvider>\n  </React.StrictMode>\n);\n"
  },
  {
    "path": "client/src/responsive.scss",
    "content": "@mixin sm {\n  @media (max-width: 738px) {\n    @content;\n  }\n}\n\n@mixin md {\n  @media (max-width: 1024px) {\n    @content;\n  }\n}\n\n@mixin lg {\n  @media (max-width: 1366px) {\n    @content;\n  }\n}\n"
  },
  {
    "path": "client/src/routes/homePage/homePage.jsx",
    "content": "import { useContext } from \"react\";\nimport SearchBar from \"../../components/searchBar/SearchBar\";\nimport \"./homePage.scss\";\nimport { AuthContext } from \"../../context/AuthContext\";\n\nfunction HomePage() {\n\n  const {currentUser} = useContext(AuthContext)\n\n  return (\n    <div className=\"homePage\">\n      <div className=\"textContainer\">\n        <div className=\"wrapper\">\n          <h1 className=\"title\">Find Real Estate & Get Your Dream Place</h1>\n          <p>\n            Lorem ipsum dolor sit amet consectetur adipisicing elit. Eos\n            explicabo suscipit cum eius, iure est nulla animi consequatur\n            facilis id pariatur fugit quos laudantium temporibus dolor ea\n            repellat provident impedit!\n          </p>\n          <SearchBar />\n          <div className=\"boxes\">\n            <div className=\"box\">\n              <h1>16+</h1>\n              <h2>Years of Experience</h2>\n            </div>\n            <div className=\"box\">\n              <h1>200</h1>\n              <h2>Award Gained</h2>\n            </div>\n            <div className=\"box\">\n              <h1>2000+</h1>\n              <h2>Property Ready</h2>\n            </div>\n          </div>\n        </div>\n      </div>\n      <div className=\"imgContainer\">\n        <img src=\"/bg.png\" alt=\"\" />\n      </div>\n    </div>\n  );\n}\n\nexport default HomePage;\n"
  },
  {
    "path": "client/src/routes/homePage/homePage.scss",
    "content": "@import \"../../responsive.scss\";\n\n.homePage {\n  display: flex;\n  height: 100%;\n\n  .textContainer {\n    flex: 3;\n\n    .wrapper {\n      padding-right: 100px;\n      display: flex;\n      flex-direction: column;\n      justify-content: center;\n      gap: 50px;\n      height: 100%;\n\n      @include lg{\n        padding-right: 50px;\n      }\n\n      @include md{\n        padding: 0;\n      }\n\n      @include sm{\n        justify-content: flex-start;\n      }\n\n      .title {\n        font-size: 64px;\n\n        @include lg {\n          font-size: 48px;\n        }\n      }\n\n      .boxes {\n        display: flex;\n        justify-content: space-between;\n\n        @include sm {\n          display: none;\n        }\n\n        h1 {\n          font-size: 36px;\n\n          @include lg {\n            font-size: 32px;\n          }\n        }\n\n        h2 {\n          font-size: 20px;\n          font-weight: 300;\n        }\n      }\n    }\n  }\n\n  .imgContainer {\n    flex: 2;\n    background-color: #fcf5f3;\n    position: relative;\n    display: flex;\n    align-items: center;\n\n    @include md {\n      display: none;\n    }\n\n    img {\n      width: 115%;\n      position: absolute;\n      right: 0;\n\n      @include lg {\n        width: 105%;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "client/src/routes/layout/layout.jsx",
    "content": "import \"./layout.scss\";\nimport Navbar from \"../../components/navbar/Navbar\";\nimport { Navigate, Outlet } from \"react-router-dom\";\nimport { useContext } from \"react\";\nimport { AuthContext } from \"../../context/AuthContext\";\n\nfunction Layout() {\n  return (\n    <div className=\"layout\">\n      <div className=\"navbar\">\n        <Navbar />\n      </div>\n      <div className=\"content\">\n        <Outlet />\n      </div>\n    </div>\n  );\n}\n\nfunction RequireAuth() {\n  const { currentUser } = useContext(AuthContext);\n\n  if (!currentUser) return <Navigate to=\"/login\" />;\n  else {\n    return (\n      <div className=\"layout\">\n        <div className=\"navbar\">\n          <Navbar />\n        </div>\n        <div className=\"content\">\n          <Outlet />\n        </div>\n      </div>\n    );\n  }\n}\n\nexport { Layout, RequireAuth };\n"
  },
  {
    "path": "client/src/routes/layout/layout.scss",
    "content": "@import \"../../responsive.scss\";\n\n.layout {\n  height: 100vh;\n  max-width: 1366px;\n  margin-left: auto;\n  margin-right: auto;\n  padding-left: 20px;\n  padding-right: 20px;\n  display: flex;\n  flex-direction: column;\n\n  @include lg {\n    // background-color: rgb(247, 210, 196);\n    max-width: 1280px;\n  }\n\n  @include md {\n    // background-color: rgb(186, 203, 234);\n    max-width: 768px;\n  }\n\n  @include sm {\n    // background-color: rgb(239, 200, 200);\n    max-width: 640px;\n  }\n\n  .content {\n    // flex: 1;\n    height: calc(100vh - 100px);\n  }\n}\n"
  },
  {
    "path": "client/src/routes/listPage/listPage.jsx",
    "content": "import \"./listPage.scss\";\nimport Filter from \"../../components/filter/Filter\";\nimport Card from \"../../components/card/Card\";\nimport Map from \"../../components/map/Map\";\nimport { Await, useLoaderData } from \"react-router-dom\";\nimport { Suspense } from \"react\";\n\nfunction ListPage() {\n  const data = useLoaderData();\n\n  return (\n    <div className=\"listPage\">\n      <div className=\"listContainer\">\n        <div className=\"wrapper\">\n          <Filter />\n          <Suspense fallback={<p>Loading...</p>}>\n            <Await\n              resolve={data.postResponse}\n              errorElement={<p>Error loading posts!</p>}\n            >\n              {(postResponse) =>\n                postResponse.data.map((post) => (\n                  <Card key={post.id} item={post} />\n                ))\n              }\n            </Await>\n          </Suspense>\n        </div>\n      </div>\n      <div className=\"mapContainer\">\n        <Suspense fallback={<p>Loading...</p>}>\n          <Await\n            resolve={data.postResponse}\n            errorElement={<p>Error loading posts!</p>}\n          >\n            {(postResponse) => <Map items={postResponse.data} />}\n          </Await>\n        </Suspense>\n      </div>\n    </div>\n  );\n}\n\nexport default ListPage;\n"
  },
  {
    "path": "client/src/routes/listPage/listPage.scss",
    "content": "@import \"../../responsive.scss\";\n\n.listPage {\n  display: flex;\n  height: 100%;\n\n  .listContainer {\n    flex: 3;\n    height: 100%;\n\n    .wrapper{\n      height: 100%;\n      padding-right: 50px;\n      display: flex;\n      flex-direction: column;\n      gap: 50px;\n      overflow-y: scroll;\n      padding-bottom: 50px;\n    }\n  }\n  .mapContainer {\n    flex: 2;\n    height: 100%;\n    background-color: #fcf5f3;\n\n    @include md{\n      display: none;\n    }\n  }\n}\n"
  },
  {
    "path": "client/src/routes/login/login.jsx",
    "content": "import { useContext, useState } from \"react\";\nimport \"./login.scss\";\nimport { Link, useNavigate } from \"react-router-dom\";\nimport apiRequest from \"../../lib/apiRequest\";\nimport { AuthContext } from \"../../context/AuthContext\";\n\nfunction Login() {\n  const [error, setError] = useState(\"\");\n  const [isLoading, setIsLoading] = useState(false);\n\n  const {updateUser} = useContext(AuthContext)\n\n  const navigate = useNavigate();\n\n  const handleSubmit = async (e) => {\n    e.preventDefault();\n    setIsLoading(true);\n    setError(\"\");\n    const formData = new FormData(e.target);\n\n    const username = formData.get(\"username\");\n    const password = formData.get(\"password\");\n\n    try {\n      const res = await apiRequest.post(\"/auth/login\", {\n        username,\n        password,\n      });\n\n      updateUser(res.data)\n\n      navigate(\"/\");\n    } catch (err) {\n      setError(err.response.data.message);\n    } finally {\n      setIsLoading(false);\n    }\n  };\n  return (\n    <div className=\"login\">\n      <div className=\"formContainer\">\n        <form onSubmit={handleSubmit}>\n          <h1>Welcome back</h1>\n          <input\n            name=\"username\"\n            required\n            minLength={3}\n            maxLength={20}\n            type=\"text\"\n            placeholder=\"Username\"\n          />\n          <input\n            name=\"password\"\n            type=\"password\"\n            required\n            placeholder=\"Password\"\n          />\n          <button disabled={isLoading}>Login</button>\n          {error && <span>{error}</span>}\n          <Link to=\"/register\">{\"Don't\"} you have an account?</Link>\n        </form>\n      </div>\n      <div className=\"imgContainer\">\n        <img src=\"/bg.png\" alt=\"\" />\n      </div>\n    </div>\n  );\n}\n\nexport default Login;\n"
  },
  {
    "path": "client/src/routes/login/login.scss",
    "content": ".login {\r\n  height: 100%;\r\n  display: flex;\r\n\r\n  .formContainer {\r\n    flex: 3;\r\n    height: 100%;\r\n    display: flex;\r\n    align-items: center;\r\n    justify-content: center;\r\n\r\n    form {\r\n      display: flex;\r\n      flex-direction: column;\r\n      gap: 20px;\r\n\r\n      input{\r\n        padding: 20px;\r\n        border: 1px solid gray;\r\n        border-radius: 5px;\r\n      }\r\n\r\n      button{\r\n        padding: 20px;\r\n        border-radius: 5px;\r\n        border: none;\r\n        background-color: teal;\r\n        color: white;\r\n        font-weight: bold;\r\n        cursor: pointer;\r\n\r\n        &:disabled{\r\n          background-color: #BED9D8;\r\n          cursor: not-allowed;\r\n        }\r\n      }\r\n\r\n      span{\r\n        color: rgba(255, 0, 0, 0.591);\r\n      }\r\n\r\n      a{\r\n        font-size: 14px;\r\n        color: gray;\r\n        border-bottom: 1px solid gray;\r\n        width: max-content;\r\n      }\r\n    }\r\n  }\r\n\r\n  .imgContainer {\r\n    flex: 2;\r\n    background-color: #fcf5f3;\r\n    display: flex;\r\n    align-items: center;\r\n    justify-content: center;\r\n    img {\r\n      width: 100%;\r\n    }\r\n  }\r\n}\r\n"
  },
  {
    "path": "client/src/routes/newPostPage/newPostPage.jsx",
    "content": "import { useState } from \"react\";\nimport \"./newPostPage.scss\";\nimport ReactQuill from \"react-quill\";\nimport \"react-quill/dist/quill.snow.css\";\nimport apiRequest from \"../../lib/apiRequest\";\nimport UploadWidget from \"../../components/uploadWidget/UploadWidget\";\nimport { useNavigate } from \"react-router-dom\";\n\nfunction NewPostPage() {\n  const [value, setValue] = useState(\"\");\n  const [images, setImages] = useState([]);\n  const [error, setError] = useState(\"\");\n\n  const navigate = useNavigate()\n\n  const handleSubmit = async (e) => {\n    e.preventDefault();\n    const formData = new FormData(e.target);\n    const inputs = Object.fromEntries(formData);\n\n    try {\n      const res = await apiRequest.post(\"/posts\", {\n        postData: {\n          title: inputs.title,\n          price: parseInt(inputs.price),\n          address: inputs.address,\n          city: inputs.city,\n          bedroom: parseInt(inputs.bedroom),\n          bathroom: parseInt(inputs.bathroom),\n          type: inputs.type,\n          property: inputs.property,\n          latitude: inputs.latitude,\n          longitude: inputs.longitude,\n          images: images,\n        },\n        postDetail: {\n          desc: value,\n          utilities: inputs.utilities,\n          pet: inputs.pet,\n          income: inputs.income,\n          size: parseInt(inputs.size),\n          school: parseInt(inputs.school),\n          bus: parseInt(inputs.bus),\n          restaurant: parseInt(inputs.restaurant),\n        },\n      });\n      navigate(\"/\"+res.data.id)\n    } catch (err) {\n      console.log(err);\n      setError(error);\n    }\n  };\n\n  return (\n    <div className=\"newPostPage\">\n      <div className=\"formContainer\">\n        <h1>Add New Post</h1>\n        <div className=\"wrapper\">\n          <form onSubmit={handleSubmit}>\n            <div className=\"item\">\n              <label htmlFor=\"title\">Title</label>\n              <input id=\"title\" name=\"title\" type=\"text\" />\n            </div>\n            <div className=\"item\">\n              <label htmlFor=\"price\">Price</label>\n              <input id=\"price\" name=\"price\" type=\"number\" />\n            </div>\n            <div className=\"item\">\n              <label htmlFor=\"address\">Address</label>\n              <input id=\"address\" name=\"address\" type=\"text\" />\n            </div>\n            <div className=\"item description\">\n              <label htmlFor=\"desc\">Description</label>\n              <ReactQuill theme=\"snow\" onChange={setValue} value={value} />\n            </div>\n            <div className=\"item\">\n              <label htmlFor=\"city\">City</label>\n              <input id=\"city\" name=\"city\" type=\"text\" />\n            </div>\n            <div className=\"item\">\n              <label htmlFor=\"bedroom\">Bedroom Number</label>\n              <input min={1} id=\"bedroom\" name=\"bedroom\" type=\"number\" />\n            </div>\n            <div className=\"item\">\n              <label htmlFor=\"bathroom\">Bathroom Number</label>\n              <input min={1} id=\"bathroom\" name=\"bathroom\" type=\"number\" />\n            </div>\n            <div className=\"item\">\n              <label htmlFor=\"latitude\">Latitude</label>\n              <input id=\"latitude\" name=\"latitude\" type=\"text\" />\n            </div>\n            <div className=\"item\">\n              <label htmlFor=\"longitude\">Longitude</label>\n              <input id=\"longitude\" name=\"longitude\" type=\"text\" />\n            </div>\n            <div className=\"item\">\n              <label htmlFor=\"type\">Type</label>\n              <select name=\"type\">\n                <option value=\"rent\" defaultChecked>\n                  Rent\n                </option>\n                <option value=\"buy\">Buy</option>\n              </select>\n            </div>\n            <div className=\"item\">\n              <label htmlFor=\"type\">Property</label>\n              <select name=\"property\">\n                <option value=\"apartment\">Apartment</option>\n                <option value=\"house\">House</option>\n                <option value=\"condo\">Condo</option>\n                <option value=\"land\">Land</option>\n              </select>\n            </div>\n\n            <div className=\"item\">\n              <label htmlFor=\"utilities\">Utilities Policy</label>\n              <select name=\"utilities\">\n                <option value=\"owner\">Owner is responsible</option>\n                <option value=\"tenant\">Tenant is responsible</option>\n                <option value=\"shared\">Shared</option>\n              </select>\n            </div>\n            <div className=\"item\">\n              <label htmlFor=\"pet\">Pet Policy</label>\n              <select name=\"pet\">\n                <option value=\"allowed\">Allowed</option>\n                <option value=\"not-allowed\">Not Allowed</option>\n              </select>\n            </div>\n            <div className=\"item\">\n              <label htmlFor=\"income\">Income Policy</label>\n              <input\n                id=\"income\"\n                name=\"income\"\n                type=\"text\"\n                placeholder=\"Income Policy\"\n              />\n            </div>\n            <div className=\"item\">\n              <label htmlFor=\"size\">Total Size (sqft)</label>\n              <input min={0} id=\"size\" name=\"size\" type=\"number\" />\n            </div>\n            <div className=\"item\">\n              <label htmlFor=\"school\">School</label>\n              <input min={0} id=\"school\" name=\"school\" type=\"number\" />\n            </div>\n            <div className=\"item\">\n              <label htmlFor=\"bus\">bus</label>\n              <input min={0} id=\"bus\" name=\"bus\" type=\"number\" />\n            </div>\n            <div className=\"item\">\n              <label htmlFor=\"restaurant\">Restaurant</label>\n              <input min={0} id=\"restaurant\" name=\"restaurant\" type=\"number\" />\n            </div>\n            <button className=\"sendButton\">Add</button>\n            {error && <span>error</span>}\n          </form>\n        </div>\n      </div>\n      <div className=\"sideContainer\">\n        {images.map((image, index) => (\n          <img src={image} key={index} alt=\"\" />\n        ))}\n        <UploadWidget\n          uwConfig={{\n            multiple: true,\n            cloudName: \"lamadev\",\n            uploadPreset: \"estate\",\n            folder: \"posts\",\n          }}\n          setState={setImages}\n        />\n      </div>\n    </div>\n  );\n}\n\nexport default NewPostPage;\n"
  },
  {
    "path": "client/src/routes/newPostPage/newPostPage.scss",
    "content": ".newPostPage {\n  height: 100%;\n  display: flex;\n\n  .formContainer {\n    flex: 3;\n    overflow: scroll;\n\n    .wrapper {\n      margin: 30px 50px 100px 0px;\n\n      form {\n        display: flex;\n        justify-content: space-between;\n        flex-wrap: wrap;\n        gap: 20px;\n\n        .item {\n          width: 30%;\n          display: flex;\n          flex-direction: column;\n          gap: 5px;\n\n          label {\n          }\n\n          input {\n            padding: 20px;\n            border-radius: 5px;\n            border: 1px solid gray;\n          }\n\n          select {\n            padding: 19px;\n          }\n\n          &.description {\n            width: 100%;\n            height: 320px;\n\n            .quill > .ql-container > .ql-editor{\n              height: 200px;\n              font-size: 16px;\n            }\n          }\n        }\n        .sendButton {\n          width: 30%;\n          border-radius: 5px;\n          border: none;\n          background-color: teal;\n          color: white;\n          font-weight: bold;\n          cursor: pointer;\n        }\n      }\n    }\n  }\n\n  .sideContainer {\n    flex: 2;\n    background-color: #fcf5f3;\n    display: flex;\n    flex-direction: column;\n    gap: 20px;\n    align-items: center;\n    justify-content: center;\n\n    img {\n      width: 50%;\n      height: 180px;\n      object-fit: cover;\n      border-radius: 5px;\n    }\n  }\n}\n"
  },
  {
    "path": "client/src/routes/profilePage/profilePage.jsx",
    "content": "import Chat from \"../../components/chat/Chat\";\nimport List from \"../../components/list/List\";\nimport \"./profilePage.scss\";\nimport apiRequest from \"../../lib/apiRequest\";\nimport { Await, Link, useLoaderData, useNavigate } from \"react-router-dom\";\nimport { Suspense, useContext } from \"react\";\nimport { AuthContext } from \"../../context/AuthContext\";\n\nfunction ProfilePage() {\n  const data = useLoaderData();\n\n  const { updateUser, currentUser } = useContext(AuthContext);\n\n  const navigate = useNavigate();\n\n  const handleLogout = async () => {\n    try {\n      await apiRequest.post(\"/auth/logout\");\n      updateUser(null);\n      navigate(\"/\");\n    } catch (err) {\n      console.log(err);\n    }\n  };\n  return (\n    <div className=\"profilePage\">\n      <div className=\"details\">\n        <div className=\"wrapper\">\n          <div className=\"title\">\n            <h1>User Information</h1>\n            <Link to=\"/profile/update\">\n              <button>Update Profile</button>\n            </Link>\n          </div>\n          <div className=\"info\">\n            <span>\n              Avatar:\n              <img src={currentUser.avatar || \"noavatar.jpg\"} alt=\"\" />\n            </span>\n            <span>\n              Username: <b>{currentUser.username}</b>\n            </span>\n            <span>\n              E-mail: <b>{currentUser.email}</b>\n            </span>\n            <button onClick={handleLogout}>Logout</button>\n          </div>\n          <div className=\"title\">\n            <h1>My List</h1>\n            <Link to=\"/add\">\n              <button>Create New Post</button>\n            </Link>\n          </div>\n          <Suspense fallback={<p>Loading...</p>}>\n            <Await\n              resolve={data.postResponse}\n              errorElement={<p>Error loading posts!</p>}\n            >\n              {(postResponse) => <List posts={postResponse.data.userPosts} />}\n            </Await>\n          </Suspense>\n          <div className=\"title\">\n            <h1>Saved List</h1>\n          </div>\n          <Suspense fallback={<p>Loading...</p>}>\n            <Await\n              resolve={data.postResponse}\n              errorElement={<p>Error loading posts!</p>}\n            >\n              {(postResponse) => <List posts={postResponse.data.savedPosts} />}\n            </Await>\n          </Suspense>\n        </div>\n      </div>\n      <div className=\"chatContainer\">\n        <div className=\"wrapper\">\n          <Suspense fallback={<p>Loading...</p>}>\n            <Await\n              resolve={data.chatResponse}\n              errorElement={<p>Error loading chats!</p>}\n            >\n              {(chatResponse) => <Chat chats={chatResponse.data}/>}\n            </Await>\n          </Suspense>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default ProfilePage;\n"
  },
  {
    "path": "client/src/routes/profilePage/profilePage.scss",
    "content": "@import \"../../responsive.scss\";\n\n.profilePage {\n  display: flex;\n  height: 100%;\n\n  @include md{\n    flex-direction: column;\n    overflow: scroll;\n  }\n\n  .details {\n    flex: 3;\n    overflow-y: scroll;\n    padding-bottom: 50px;\n\n    @include md{\n      flex:none;\n      height: max-content;\n    }\n\n    .wrapper{\n      padding-right: 50px;\n      display: flex;\n      flex-direction: column;\n      gap: 50px;\n\n      .title{\n        display: flex;\n        align-items: center;\n        justify-content: space-between;\n\n        h1{\n          font-weight: 300;\n        }\n\n        button{\n          padding: 12px 24px;\n          background-color: #fece51;\n          cursor: pointer;\n          border: none;\n        }\n      }\n\n      .info{\n        display: flex;\n        flex-direction: column;\n        gap: 20px;\n\n        span{\n          display: flex;\n          align-items: center;\n          gap: 20px;\n        }\n\n        button{\n          width: 100px;\n          background-color: teal;\n          border: none;\n          color: white;\n          padding: 10px 20px;\n          cursor: pointer;\n          border-radius: 5px;\n        }\n\n        img{\n          width: 40px;\n          height: 40px;\n          border-radius: 50%;\n          object-fit: cover;\n        }\n      }\n    }\n  }\n  .chatContainer {\n    flex: 2;\n    background-color: #fcf5f3;\n    height: 100%;\n\n    @include md{\n      flex:none;\n      height: max-content;\n    }\n\n    .wrapper{\n      padding: 0px 20px;\n      height: 100%;\n    }\n  }\n}\n"
  },
  {
    "path": "client/src/routes/profileUpdatePage/profileUpdatePage.jsx",
    "content": "import { useContext, useState } from \"react\";\nimport \"./profileUpdatePage.scss\";\nimport { AuthContext } from \"../../context/AuthContext\";\nimport apiRequest from \"../../lib/apiRequest\";\nimport { useNavigate } from \"react-router-dom\";\nimport UploadWidget from \"../../components/uploadWidget/UploadWidget\";\n\nfunction ProfileUpdatePage() {\n  const { currentUser, updateUser } = useContext(AuthContext);\n  const [error, setError] = useState(\"\");\n  const [avatar, setAvatar] = useState([]);\n\n  const navigate = useNavigate();\n\n  const handleSubmit = async (e) => {\n    e.preventDefault();\n    const formData = new FormData(e.target);\n\n    const { username, email, password } = Object.fromEntries(formData);\n\n    try {\n      const res = await apiRequest.put(`/users/${currentUser.id}`, {\n        username,\n        email,\n        password,\n        avatar:avatar[0]\n      });\n      updateUser(res.data);\n      navigate(\"/profile\");\n    } catch (err) {\n      console.log(err);\n      setError(err.response.data.message);\n    }\n  };\n\n  return (\n    <div className=\"profileUpdatePage\">\n      <div className=\"formContainer\">\n        <form onSubmit={handleSubmit}>\n          <h1>Update Profile</h1>\n          <div className=\"item\">\n            <label htmlFor=\"username\">Username</label>\n            <input\n              id=\"username\"\n              name=\"username\"\n              type=\"text\"\n              defaultValue={currentUser.username}\n            />\n          </div>\n          <div className=\"item\">\n            <label htmlFor=\"email\">Email</label>\n            <input\n              id=\"email\"\n              name=\"email\"\n              type=\"email\"\n              defaultValue={currentUser.email}\n            />\n          </div>\n          <div className=\"item\">\n            <label htmlFor=\"password\">Password</label>\n            <input id=\"password\" name=\"password\" type=\"password\" />\n          </div>\n          <button>Update</button>\n          {error && <span>error</span>}\n        </form>\n      </div>\n      <div className=\"sideContainer\">\n        <img src={avatar[0] || currentUser.avatar || \"/noavatar.jpg\"} alt=\"\" className=\"avatar\" />\n        <UploadWidget\n          uwConfig={{\n            cloudName: \"lamadev\",\n            uploadPreset: \"estate\",\n            multiple: false,\n            maxImageFileSize: 2000000,\n            folder: \"avatars\",\n          }}\n          setState={setAvatar}\n        />\n      </div>\n    </div>\n  );\n}\n\nexport default ProfileUpdatePage;\n"
  },
  {
    "path": "client/src/routes/profileUpdatePage/profileUpdatePage.scss",
    "content": ".profileUpdatePage {\n  height: 100%;\n  display: flex;\n\n  .formContainer {\n    flex:3;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    form {\n      display: flex;\n      flex-direction: column;\n      gap: 20px;\n\n      .item {\n        display: flex;\n        flex-direction: column;\n        gap: 5px;\n\n        label {\n        }\n\n        input {\n          padding: 20px;\n          border-radius: 5px;\n          border: 1px solid gray;\n        }\n      }\n      button {\n        padding: 20px;\n        border-radius: 5px;\n        border: none;\n        background-color: teal;\n        color: white;\n        font-weight: bold;\n        cursor: pointer;\n      }\n    }\n  }\n\n  .sideContainer {\n    flex: 2;\n    background-color: #fcf5f3;\n    display: flex;\n    flex-direction: column;\n    gap: 20px;\n    align-items: center;\n    justify-content: center;\n\n    .avatar{\n      width: 50%;\n      object-fit: cover;\n    }\n  }\n}\n"
  },
  {
    "path": "client/src/routes/register/register.jsx",
    "content": "import \"./register.scss\";\nimport { Link, useNavigate } from \"react-router-dom\";\nimport axios from \"axios\";\nimport { useState } from \"react\";\nimport apiRequest from \"../../lib/apiRequest\";\n\nfunction Register() {\n  const [error, setError] = useState(\"\");\n  const [isLoading, setIsLoading] = useState(false);\n\n  const navigate = useNavigate();\n\n  const handleSubmit = async (e) => {\n    e.preventDefault();\n    setError(\"\")\n    setIsLoading(true);\n    const formData = new FormData(e.target);\n\n    const username = formData.get(\"username\");\n    const email = formData.get(\"email\");\n    const password = formData.get(\"password\");\n\n    try {\n      const res = await apiRequest.post(\"/auth/register\", {\n        username,\n        email,\n        password,\n      });\n\n      navigate(\"/login\");\n    } catch (err) {\n      setError(err.response.data.message);\n    } finally {\n      setIsLoading(false);\n    }\n  };\n  return (\n    <div className=\"registerPage\">\n      <div className=\"formContainer\">\n        <form onSubmit={handleSubmit}>\n          <h1>Create an Account</h1>\n          <input name=\"username\" type=\"text\" placeholder=\"Username\" />\n          <input name=\"email\" type=\"text\" placeholder=\"Email\" />\n          <input name=\"password\" type=\"password\" placeholder=\"Password\" />\n          <button disabled={isLoading}>Register</button>\n          {error && <span>{error}</span>}\n          <Link to=\"/login\">Do you have an account?</Link>\n        </form>\n      </div>\n      <div className=\"imgContainer\">\n        <img src=\"/bg.png\" alt=\"\" />\n      </div>\n    </div>\n  );\n}\n\nexport default Register;\n"
  },
  {
    "path": "client/src/routes/register/register.scss",
    "content": ".registerPage {\n  height: 100%;\n  display: flex;\n\n  .formContainer {\n    flex: 3;\n    height: 100%;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n\n    form {\n      display: flex;\n      flex-direction: column;\n      gap: 20px;\n\n      input {\n        padding: 20px;\n        border: 1px solid gray;\n        border-radius: 5px;\n      }\n\n      button {\n        padding: 20px;\n        border-radius: 5px;\n        border: none;\n        background-color: teal;\n        color: white;\n        font-weight: bold;\n        cursor: pointer;\n\n        &:disabled {\n          background-color: #bed9d8;\n          cursor: not-allowed;\n        }\n      }\n\n      span {\n        color: rgba(255, 0, 0, 0.591);\n      }\n\n      a {\n        font-size: 14px;\n        color: gray;\n        border-bottom: 1px solid gray;\n        width: max-content;\n      }\n    }\n  }\n\n  .imgContainer {\n    flex: 2;\n    background-color: #fcf5f3;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    img {\n      width: 100%;\n    }\n  }\n}\n"
  },
  {
    "path": "client/src/routes/singlePage/singlePage.jsx",
    "content": "import \"./singlePage.scss\";\nimport Slider from \"../../components/slider/Slider\";\nimport Map from \"../../components/map/Map\";\nimport { useNavigate, useLoaderData } from \"react-router-dom\";\nimport DOMPurify from \"dompurify\";\nimport { useContext, useState } from \"react\";\nimport { AuthContext } from \"../../context/AuthContext\";\nimport apiRequest from \"../../lib/apiRequest\";\n\nfunction SinglePage() {\n  const post = useLoaderData();\n  const [saved, setSaved] = useState(post.isSaved);\n  const { currentUser } = useContext(AuthContext);\n  const navigate = useNavigate();\n\n  const handleSave = async () => {\n    if (!currentUser) {\n      navigate(\"/login\");\n    }\n    // AFTER REACT 19 UPDATE TO USEOPTIMISTIK HOOK\n    setSaved((prev) => !prev);\n    try {\n      await apiRequest.post(\"/users/save\", { postId: post.id });\n    } catch (err) {\n      console.log(err);\n      setSaved((prev) => !prev);\n    }\n  };\n\n  return (\n    <div className=\"singlePage\">\n      <div className=\"details\">\n        <div className=\"wrapper\">\n          <Slider images={post.images} />\n          <div className=\"info\">\n            <div className=\"top\">\n              <div className=\"post\">\n                <h1>{post.title}</h1>\n                <div className=\"address\">\n                  <img src=\"/pin.png\" alt=\"\" />\n                  <span>{post.address}</span>\n                </div>\n                <div className=\"price\">$ {post.price}</div>\n              </div>\n              <div className=\"user\">\n                <img src={post.user.avatar} alt=\"\" />\n                <span>{post.user.username}</span>\n              </div>\n            </div>\n            <div\n              className=\"bottom\"\n              dangerouslySetInnerHTML={{\n                __html: DOMPurify.sanitize(post.postDetail.desc),\n              }}\n            ></div>\n          </div>\n        </div>\n      </div>\n      <div className=\"features\">\n        <div className=\"wrapper\">\n          <p className=\"title\">General</p>\n          <div className=\"listVertical\">\n            <div className=\"feature\">\n              <img src=\"/utility.png\" alt=\"\" />\n              <div className=\"featureText\">\n                <span>Utilities</span>\n                {post.postDetail.utilities === \"owner\" ? (\n                  <p>Owner is responsible</p>\n                ) : (\n                  <p>Tenant is responsible</p>\n                )}\n              </div>\n            </div>\n            <div className=\"feature\">\n              <img src=\"/pet.png\" alt=\"\" />\n              <div className=\"featureText\">\n                <span>Pet Policy</span>\n                {post.postDetail.pet === \"allowed\" ? (\n                  <p>Pets Allowed</p>\n                ) : (\n                  <p>Pets not Allowed</p>\n                )}\n              </div>\n            </div>\n            <div className=\"feature\">\n              <img src=\"/fee.png\" alt=\"\" />\n              <div className=\"featureText\">\n                <span>Income Policy</span>\n                <p>{post.postDetail.income}</p>\n              </div>\n            </div>\n          </div>\n          <p className=\"title\">Sizes</p>\n          <div className=\"sizes\">\n            <div className=\"size\">\n              <img src=\"/size.png\" alt=\"\" />\n              <span>{post.postDetail.size} sqft</span>\n            </div>\n            <div className=\"size\">\n              <img src=\"/bed.png\" alt=\"\" />\n              <span>{post.bedroom} beds</span>\n            </div>\n            <div className=\"size\">\n              <img src=\"/bath.png\" alt=\"\" />\n              <span>{post.bathroom} bathroom</span>\n            </div>\n          </div>\n          <p className=\"title\">Nearby Places</p>\n          <div className=\"listHorizontal\">\n            <div className=\"feature\">\n              <img src=\"/school.png\" alt=\"\" />\n              <div className=\"featureText\">\n                <span>School</span>\n                <p>\n                  {post.postDetail.school > 999\n                    ? post.postDetail.school / 1000 + \"km\"\n                    : post.postDetail.school + \"m\"}{\" \"}\n                  away\n                </p>\n              </div>\n            </div>\n            <div className=\"feature\">\n              <img src=\"/pet.png\" alt=\"\" />\n              <div className=\"featureText\">\n                <span>Bus Stop</span>\n                <p>{post.postDetail.bus}m away</p>\n              </div>\n            </div>\n            <div className=\"feature\">\n              <img src=\"/fee.png\" alt=\"\" />\n              <div className=\"featureText\">\n                <span>Restaurant</span>\n                <p>{post.postDetail.restaurant}m away</p>\n              </div>\n            </div>\n          </div>\n          <p className=\"title\">Location</p>\n          <div className=\"mapContainer\">\n            <Map items={[post]} />\n          </div>\n          <div className=\"buttons\">\n            <button>\n              <img src=\"/chat.png\" alt=\"\" />\n              Send a Message\n            </button>\n            <button\n              onClick={handleSave}\n              style={{\n                backgroundColor: saved ? \"#fece51\" : \"white\",\n              }}\n            >\n              <img src=\"/save.png\" alt=\"\" />\n              {saved ? \"Place Saved\" : \"Save the Place\"}\n            </button>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default SinglePage;\n"
  },
  {
    "path": "client/src/routes/singlePage/singlePage.scss",
    "content": "@import \"../../responsive.scss\";\n\n.singlePage {\n  display: flex;\n  height: 100%;\n\n  @include md {\n    flex-direction: column;\n    overflow: scroll;\n  }\n\n  .details {\n    flex: 3;\n    height: 100%;\n    overflow-y: scroll;\n\n    @include md {\n      flex: none;\n      height: max-content;\n      margin-bottom: 50px;\n    }\n\n    .wrapper {\n      padding-right: 50px;\n\n      @include lg {\n        padding-right: 20px;\n      }\n      @include md {\n        padding-right: 0px;\n      }\n\n      .info {\n        margin-top: 50px;\n        .top {\n          display: flex;\n          justify-content: space-between;\n\n          @include sm {\n            flex-direction: column;\n            gap: 20px;\n          }\n\n          .post {\n            display: flex;\n            flex-direction: column;\n            gap: 20px;\n            h1 {\n              font-weight: 400;\n            }\n            .address {\n              display: flex;\n              gap: 5px;\n              align-items: center;\n              color: #888;\n              font-size: 14px;\n\n              img {\n                width: 16px;\n                height: 16px;\n              }\n            }\n            .price {\n              padding: 5px;\n              background-color: rgba(254, 205, 81, 0.438);\n              border-radius: 5px;\n              width: max-content;\n              font-size: 20px;\n              font-weight: 300;\n            }\n          }\n          .user {\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            gap: 20px;\n            padding: 0px 50px;\n            border-radius: 10px;\n            background-color: rgba(254, 205, 81, 0.209);\n            font-weight: 600;\n\n            @include sm {\n              padding: 20px 50px;\n            }\n\n            img {\n              width: 50px;\n              height: 50px;\n              border-radius: 50%;\n              object-fit: cover;\n            }\n          }\n        }\n        .bottom {\n          margin-top: 50px;\n          color: #555;\n          line-height: 20px;\n        }\n      }\n    }\n  }\n  .features {\n    flex: 2;\n    background-color: #fcf5f3;\n    height: 100%;\n    overflow-y: scroll;\n\n    @include md {\n      flex: none;\n      height: max-content;\n      margin-bottom: 50px;\n    }\n\n    .wrapper {\n      padding: 0px 20px;\n      display: flex;\n      flex-direction: column;\n      gap: 20px;\n\n      @include md {\n        padding: 20px;\n      }\n\n      img {\n        width: 24px;\n        height: 24px;\n      }\n\n      .title {\n        font-weight: bold;\n        font-size: 18px;\n        margin-bottom: 10px;\n      }\n\n      .feature {\n        display: flex;\n        align-items: center;\n        gap: 10px;\n\n        img {\n          background-color: rgba(254, 205, 81, 0.209);\n        }\n\n        .featureText {\n          span {\n            font-weight: bold;\n          }\n          p {\n            font-size: 14px;\n          }\n        }\n      }\n\n      .listVertical {\n        display: flex;\n        flex-direction: column;\n        gap: 20px;\n        padding: 20px 10px;\n        background-color: white;\n        border-radius: 10px;\n      }\n      .listHorizontal {\n        display: flex;\n        justify-content: space-between;\n        padding: 20px 10px;\n        background-color: white;\n        border-radius: 10px;\n      }\n\n      .sizes {\n        display: flex;\n        justify-content: space-between;\n\n        @include lg {\n          font-size: 12px;\n        }\n\n        .size {\n          display: flex;\n          align-items: center;\n          gap: 10px;\n          background-color: white;\n          padding: 10px;\n          border-radius: 5px;\n        }\n      }\n\n      .mapContainer {\n        width: 100%;\n        height: 200px;\n      }\n\n      .buttons {\n        display: flex;\n        justify-content: space-between;\n\n        button {\n          padding: 20px;\n          display: flex;\n          align-items: center;\n          gap: 5px;\n          background-color: white;\n          border: 1px solid #fece51;\n          border-radius: 5px;\n          cursor: pointer;\n\n          img {\n            width: 16px;\n            height: 16px;\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "client/vite.config.js",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "socket/.gitignore",
    "content": "node_modules"
  },
  {
    "path": "socket/app.js",
    "content": "import { Server } from \"socket.io\";\n\nconst io = new Server({\n  cors: {\n    origin: \"http://localhost:5173\",\n  },\n});\n\nlet onlineUser = [];\n\nconst addUser = (userId, socketId) => {\n  const userExits = onlineUser.find((user) => user.userId === userId);\n  if (!userExits) {\n    onlineUser.push({ userId, socketId });\n  }\n};\n\nconst removeUser = (socketId) => {\n  onlineUser = onlineUser.filter((user) => user.socketId !== socketId);\n};\n\nconst getUser = (userId) => {\n  return onlineUser.find((user) => user.userId === userId);\n};\n\nio.on(\"connection\", (socket) => {\n  socket.on(\"newUser\", (userId) => {\n    addUser(userId, socket.id);\n  });\n\n  socket.on(\"sendMessage\", ({ receiverId, data }) => {\n    const receiver = getUser(receiverId);\n    io.to(receiver.socketId).emit(\"getMessage\", data);\n  });\n\n  socket.on(\"disconnect\", () => {\n    removeUser(socket.id);\n  });\n});\n\nio.listen(\"4000\");\n"
  },
  {
    "path": "socket/package.json",
    "content": "{\n  \"name\": \"socket\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"app.js\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"socket.io\": \"^4.7.5\"\n  }\n}\n"
  }
]