├── Client
├── .env
├── assets
│ ├── icon.png
│ ├── favicon.png
│ ├── splash.png
│ └── adaptive-icon.png
├── src
│ ├── assets
│ │ ├── ssl.png
│ │ ├── logo.png
│ │ ├── logo1.png
│ │ ├── logos.png
│ │ ├── order.png
│ │ ├── visa.png
│ │ ├── address.png
│ │ ├── favorite.png
│ │ ├── feedback.png
│ │ ├── maestro.png
│ │ ├── credit_card.png
│ │ ├── emptyChart.png
│ │ ├── heart_img.png
│ │ ├── mastercard.png
│ │ └── evaluationComplete.png
│ ├── services
│ │ ├── CategoryService.js
│ │ ├── AuthService.js
│ │ ├── UserService.js
│ │ ├── CommentService.js
│ │ ├── FavoriteService.js
│ │ ├── AddressService.js
│ │ ├── BankCardService.js
│ │ ├── OrderService.js
│ │ ├── HttpConnection.js
│ │ ├── StoreService.js
│ │ └── ProductService.js
│ ├── consts
│ │ ├── colors.js
│ │ └── time.js
│ ├── view
│ │ ├── screens
│ │ │ ├── SplashScreen.js
│ │ │ ├── EditCommentScreen.js
│ │ │ ├── AddCommentScreen.js
│ │ │ ├── HomeScreen.js
│ │ │ ├── AddAddressScreen.js
│ │ │ ├── EditAddressScreen.js
│ │ │ ├── SearchResultsScreen.js
│ │ │ ├── MyOrdersScreen.js
│ │ │ ├── AccountScreen.js
│ │ │ ├── BasketScreen.js
│ │ │ ├── CategoriesScreen.js
│ │ │ ├── FavoriteListScreen.js
│ │ │ └── CheckoutScreen.js
│ │ └── components
│ │ │ ├── general
│ │ │ ├── ToastMessage.js
│ │ │ ├── ValidationModal.js
│ │ │ ├── ModalMessage.js
│ │ │ ├── CardImageSlider.js
│ │ │ └── ProductCard.js
│ │ │ ├── productDetail
│ │ │ ├── ProductBadges.js
│ │ │ ├── ImageSlider.js
│ │ │ ├── SizeOptionsSection.js
│ │ │ ├── ColorOptionsSection.js
│ │ │ ├── ProductInfo.js
│ │ │ ├── RecommendProductItem.js
│ │ │ ├── RecommendProducts.js
│ │ │ ├── ProductProperties.js
│ │ │ └── AnimatedHeader.js
│ │ │ ├── checkout
│ │ │ ├── ContractForms.js
│ │ │ ├── ConfirmationField.js
│ │ │ ├── PaymentBottomSheet.js
│ │ │ ├── SelectAddressSection.js
│ │ │ └── BankCardBottomSheet.js
│ │ │ ├── favorite
│ │ │ └── FavoritesMenu.js
│ │ │ ├── evaluation
│ │ │ └── EvaluationMenu.js
│ │ │ ├── basket
│ │ │ └── RecommendBasketItem.js
│ │ │ └── address
│ │ │ └── AddressBottomSheet.js
│ └── Context
│ │ ├── CategoryContext.js
│ │ ├── AuthContext.js
│ │ ├── FavoriteContext.js
│ │ ├── ProductContext.js
│ │ └── StoreContext.js
├── .gitignore
├── babel.config.js
├── app.json
├── App.js
└── package.json
└── Server
├── Config
└── env
│ └── config.env
├── Helpers
├── error
│ └── CustomError.js
├── database
│ └── connectDatabase.js
├── input
│ └── inputHelpers.js
├── Libraries
│ └── imageUpload.js
└── authorization
│ └── tokenHelpers.js
├── Routers
├── banner.js
├── user.js
├── auth.js
├── address.js
├── comment.js
├── bankCard.js
├── favorite.js
├── category.js
├── order.js
├── store.js
├── index.js
└── product.js
├── Models
├── lastviewed.js
├── subcategory.js
├── category.js
├── banner.js
├── evaluation.js
├── orderitem.js
├── comment.js
├── address.js
├── bankCard.js
├── product.js
├── order.js
└── user.js
├── package.json
├── server.js
├── Controllers
├── banner.js
├── user.js
├── address.js
├── favorite.js
├── category.js
├── bankCard.js
├── auth.js
├── comment.js
├── store.js
└── order.js
└── Middlewares
├── Errors
└── customErrorHandler.js
└── Authorization
└── auth.js
/Client/.env:
--------------------------------------------------------------------------------
1 | NGROK_URL =
--------------------------------------------------------------------------------
/Client/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muhammet-Yildiz/HizliSepet/HEAD/Client/assets/icon.png
--------------------------------------------------------------------------------
/Client/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muhammet-Yildiz/HizliSepet/HEAD/Client/assets/favicon.png
--------------------------------------------------------------------------------
/Client/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muhammet-Yildiz/HizliSepet/HEAD/Client/assets/splash.png
--------------------------------------------------------------------------------
/Client/src/assets/ssl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muhammet-Yildiz/HizliSepet/HEAD/Client/src/assets/ssl.png
--------------------------------------------------------------------------------
/Client/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muhammet-Yildiz/HizliSepet/HEAD/Client/src/assets/logo.png
--------------------------------------------------------------------------------
/Client/src/assets/logo1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muhammet-Yildiz/HizliSepet/HEAD/Client/src/assets/logo1.png
--------------------------------------------------------------------------------
/Client/src/assets/logos.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muhammet-Yildiz/HizliSepet/HEAD/Client/src/assets/logos.png
--------------------------------------------------------------------------------
/Client/src/assets/order.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muhammet-Yildiz/HizliSepet/HEAD/Client/src/assets/order.png
--------------------------------------------------------------------------------
/Client/src/assets/visa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muhammet-Yildiz/HizliSepet/HEAD/Client/src/assets/visa.png
--------------------------------------------------------------------------------
/Client/src/assets/address.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muhammet-Yildiz/HizliSepet/HEAD/Client/src/assets/address.png
--------------------------------------------------------------------------------
/Client/src/assets/favorite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muhammet-Yildiz/HizliSepet/HEAD/Client/src/assets/favorite.png
--------------------------------------------------------------------------------
/Client/src/assets/feedback.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muhammet-Yildiz/HizliSepet/HEAD/Client/src/assets/feedback.png
--------------------------------------------------------------------------------
/Client/src/assets/maestro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muhammet-Yildiz/HizliSepet/HEAD/Client/src/assets/maestro.png
--------------------------------------------------------------------------------
/Client/assets/adaptive-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muhammet-Yildiz/HizliSepet/HEAD/Client/assets/adaptive-icon.png
--------------------------------------------------------------------------------
/Client/src/assets/credit_card.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muhammet-Yildiz/HizliSepet/HEAD/Client/src/assets/credit_card.png
--------------------------------------------------------------------------------
/Client/src/assets/emptyChart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muhammet-Yildiz/HizliSepet/HEAD/Client/src/assets/emptyChart.png
--------------------------------------------------------------------------------
/Client/src/assets/heart_img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muhammet-Yildiz/HizliSepet/HEAD/Client/src/assets/heart_img.png
--------------------------------------------------------------------------------
/Client/src/assets/mastercard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muhammet-Yildiz/HizliSepet/HEAD/Client/src/assets/mastercard.png
--------------------------------------------------------------------------------
/Client/src/assets/evaluationComplete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muhammet-Yildiz/HizliSepet/HEAD/Client/src/assets/evaluationComplete.png
--------------------------------------------------------------------------------
/Client/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .expo/
3 | dist/
4 | npm-debug.*
5 | *.jks
6 | *.p8
7 | *.p12
8 | *.key
9 | *.mobileprovision
10 | *.orig.*
11 | web-build/
12 |
13 | # macOS
14 | .DS_Store
15 |
--------------------------------------------------------------------------------
/Server/Config/env/config.env:
--------------------------------------------------------------------------------
1 | # Server variables
2 |
3 | MONGO_URI =
4 |
5 | PORT = 5000
6 |
7 | NODE_ENV = development
8 |
9 |
10 | # Json web token
11 |
12 | JWT_SECRET_KEY =
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Client/src/services/CategoryService.js:
--------------------------------------------------------------------------------
1 | import http from "./HttpConnection";
2 |
3 | const getAllCategories = () => {
4 | return http.get("/categories/all");
5 | };
6 |
7 | export default {
8 | getAllCategories
9 | }
--------------------------------------------------------------------------------
/Server/Helpers/error/CustomError.js:
--------------------------------------------------------------------------------
1 | class CustomError extends Error {
2 |
3 | constructor(message,status ) {
4 |
5 | super(message)
6 |
7 | this.status =status ;
8 | }
9 |
10 | }
11 |
12 |
13 | module.exports = CustomError ;
--------------------------------------------------------------------------------
/Client/src/consts/colors.js:
--------------------------------------------------------------------------------
1 | const COLORS = {
2 | white: '#fff',
3 | dark: '#000',
4 | red: '#F52A2A',
5 | light: '#F1F1F1',
6 | green: '#00B761',
7 | yellow :'#F5A623',
8 | grey : '#A9A9A9',
9 | };
10 |
11 | export default COLORS;
--------------------------------------------------------------------------------
/Client/src/services/AuthService.js:
--------------------------------------------------------------------------------
1 | import http from "./HttpConnection";
2 |
3 | const login = data => {
4 | return http.post("/auth/login", data);
5 | };
6 |
7 | const register = data => {
8 | return http.post("/auth/register", data);
9 | }
10 |
11 | export default {
12 | login ,
13 | register
14 | }
--------------------------------------------------------------------------------
/Client/src/services/UserService.js:
--------------------------------------------------------------------------------
1 | import http from "./HttpConnection";
2 |
3 | const changeEmail = (data) => {
4 | return http.post("/user/changeEmail",data);
5 | };
6 |
7 | const changePassword = (data) => {
8 | return http.post("/user/changePassword",data);
9 | };
10 |
11 | export default {
12 | changeEmail,
13 | changePassword
14 | }
--------------------------------------------------------------------------------
/Client/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 | return {
4 | presets: ['babel-preset-expo'],
5 | "plugins": [
6 | [
7 | "module:react-native-dotenv",
8 | {
9 | "envName": "APP_ENV",
10 | "moduleName": "@env",
11 | "path": ".env",
12 | }
13 | ]
14 | ]
15 |
16 | };
17 | };
18 |
--------------------------------------------------------------------------------
/Client/src/consts/time.js:
--------------------------------------------------------------------------------
1 | export const MONTHS = ['OCAK', 'ŞUBAT', 'MART', 'NİSAN', 'MAYIS', 'HAZİRAN', 'TEMMUZ', 'AĞUSTOS', 'EYLÜL', 'EKİM', 'KASIM', 'ARALIK']
2 | export const LOWERMONTHS = ['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran', 'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık']
3 |
4 | export const DAYS = ['Pazartesi', 'Salı', 'Çarşamba', 'Perşembe', 'Cuma', 'Cumartesi', 'Pazar']
--------------------------------------------------------------------------------
/Server/Helpers/database/connectDatabase.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose")
2 |
3 | const connectDatabase = () => {
4 |
5 | mongoose.connect(process.env.MONGO_URI ,{useNewUrlParser : true})
6 | .then( () => {
7 | console.log("MongoDb Connection Successful")
8 | })
9 | .catch(err => {
10 | console.log(err)
11 | })
12 |
13 | }
14 |
15 | module.exports = connectDatabase
--------------------------------------------------------------------------------
/Server/Helpers/input/inputHelpers.js:
--------------------------------------------------------------------------------
1 | const bycrpt = require("bcryptjs")
2 |
3 | const validateUserInput = (email,password) => {
4 | return email && password
5 | }
6 |
7 | const comparePassword = (password ,hashedPassword) => {
8 |
9 | return bycrpt.compareSync(password,hashedPassword)
10 |
11 | }
12 |
13 |
14 | module.exports ={
15 | validateUserInput,
16 | comparePassword
17 | }
--------------------------------------------------------------------------------
/Server/Routers/banner.js:
--------------------------------------------------------------------------------
1 |
2 | const express = require('express');
3 | const {addBanner,getAllBanners} = require('../Controllers/banner');
4 | const imageUpload = require('../Helpers/Libraries/imageUpload');
5 | const router = express.Router();
6 |
7 |
8 | router.get('/all' , getAllBanners)
9 |
10 | router.post("/add" , [imageUpload.single("banner_image")] , addBanner)
11 |
12 |
13 | module.exports = router;
--------------------------------------------------------------------------------
/Server/Routers/user.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const { changeEmail ,changePassword} = require('../Controllers/user')
3 | const { getAccessToRoute } = require('../Middlewares/Authorization/auth')
4 |
5 | const router = express.Router()
6 |
7 | router.use(getAccessToRoute)
8 |
9 |
10 | router.post("/changeEmail" , changeEmail)
11 |
12 | router.post("/changePassword" , changePassword)
13 |
14 | module.exports = router
--------------------------------------------------------------------------------
/Client/src/services/CommentService.js:
--------------------------------------------------------------------------------
1 | import http from "./HttpConnection";
2 |
3 | const addComment = data => {
4 | return http.post("/comments/add", data);
5 | };
6 |
7 | const editComment = data => {
8 | return http.put("/comments/edit", data);
9 | };
10 |
11 | const deleteComment = data => {
12 | return http.post("/comments/delete", data);
13 | };
14 |
15 |
16 | export default {
17 | addComment,
18 | editComment,
19 | deleteComment
20 | }
--------------------------------------------------------------------------------
/Client/src/view/screens/SplashScreen.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { View,ActivityIndicator } from 'react-native'
3 | import COLORS from '../../consts/colors'
4 |
5 | const SplashScreen = () => {
6 | return (
7 |
8 |
9 |
10 | )
11 | }
12 |
13 | export default SplashScreen
14 |
--------------------------------------------------------------------------------
/Server/Models/lastviewed.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose')
2 |
3 | const Schema = mongoose.Schema;
4 |
5 | const lastViewedSchema = new Schema({
6 |
7 | user: {
8 | type: Schema.Types.ObjectId,
9 | ref: "User",
10 | required: true
11 | },
12 | products: [{
13 | type: Schema.Types.ObjectId,
14 | ref: "Product"
15 | }],
16 |
17 | })
18 |
19 |
20 | module.exports = mongoose.model("LastViewed", lastViewedSchema)
--------------------------------------------------------------------------------
/Client/src/services/FavoriteService.js:
--------------------------------------------------------------------------------
1 | import http from "./HttpConnection";
2 |
3 | const getAllFavoriteItems = () => {
4 | return http.get(`/favorites/all`);
5 | }
6 |
7 | const addItemToFavoritelist = (id) => {
8 | return http.post(`/favorites/${id}/add`);
9 | }
10 |
11 | const removeItemFromFavoritelist = (id) => {
12 | return http.delete(`/favorites/${id}/remove`);
13 | }
14 |
15 | export default {
16 | getAllFavoriteItems,
17 | addItemToFavoritelist,
18 | removeItemFromFavoritelist
19 | }
--------------------------------------------------------------------------------
/Server/Routers/auth.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const { register, login, getActiveUser , logout } = require('../Controllers/auth')
3 | const { getAccessToRoute } = require('../Middlewares/Authorization/auth')
4 |
5 | const router = express.Router()
6 |
7 |
8 | router.post("/register" , register)
9 |
10 | router.post("/login" , login)
11 |
12 | router.get("/logout" , getAccessToRoute, logout)
13 |
14 | router.get("/activeUser" ,getAccessToRoute , getActiveUser)
15 |
16 |
17 | module.exports = router
--------------------------------------------------------------------------------
/Server/Routers/address.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const {addAddress,editAddress,deleteAddress,getAllAddresses} = require('../Controllers/address');
3 | const { getAccessToRoute } = require('../Middlewares/Authorization/auth')
4 |
5 | const router = express.Router();
6 |
7 | router.use(getAccessToRoute)
8 |
9 | router.get('/all' , getAllAddresses)
10 |
11 | router.post('/add' , addAddress)
12 |
13 | router.put("/edit/:id" , editAddress)
14 |
15 | router.delete("/delete/:id" , deleteAddress)
16 |
17 | module.exports = router;
--------------------------------------------------------------------------------
/Server/Routers/comment.js:
--------------------------------------------------------------------------------
1 |
2 | const express = require('express');
3 | const {addComment,getAllComments ,editComment ,deleteComment} = require('../Controllers/comment');
4 | const router = express.Router();
5 | const { getAccessToRoute } = require('../Middlewares/Authorization/auth')
6 |
7 | router.use(getAccessToRoute)
8 |
9 |
10 | router.get("/all" , getAllComments)
11 |
12 | router.post("/add" , addComment)
13 |
14 | router.put("/edit" , editComment)
15 |
16 | router.post("/delete" , deleteComment)
17 |
18 |
19 | module.exports = router;
--------------------------------------------------------------------------------
/Server/Models/subcategory.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose')
2 |
3 | const Schema = mongoose.Schema;
4 |
5 | const subCategorySchema = new Schema({
6 | name : {
7 | type : String,
8 | required : [true, "Please provide a subcategory name "],
9 | },
10 | image : {
11 | type : String,
12 | },
13 | category : {
14 | type : Schema.Types.ObjectId,
15 | ref : "Category",
16 | required : true
17 | }
18 |
19 | })
20 |
21 |
22 | module.exports = mongoose.model("SubCategory", subCategorySchema)
--------------------------------------------------------------------------------
/Server/Routers/bankCard.js:
--------------------------------------------------------------------------------
1 |
2 | const express = require('express');
3 | const {editBankCard,deleteBankCard, addBankCard,getAllBankCards} = require('../Controllers/bankCard');
4 | const { getAccessToRoute } = require('../Middlewares/Authorization/auth')
5 |
6 | const router = express.Router();
7 |
8 | router.use(getAccessToRoute)
9 |
10 | router.get('/all' , getAllBankCards)
11 |
12 | router.post("/add" , addBankCard)
13 |
14 | router.put("/edit/:id" , editBankCard)
15 |
16 | router.delete("/delete/:id" , deleteBankCard)
17 |
18 |
19 | module.exports = router;
--------------------------------------------------------------------------------
/Server/Models/category.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose')
2 |
3 | const Schema = mongoose.Schema;
4 |
5 | const categorySchema = new Schema({
6 | name: {
7 | unique: true,
8 | type: String,
9 | required: [true, "Please provide a category name "],
10 | trim: true,
11 | },
12 | image: {
13 | type: String,
14 | },
15 | subCategories: [{
16 | type: Schema.Types.ObjectId,
17 | ref: "SubCategory"
18 | }]
19 |
20 | })
21 |
22 |
23 | module.exports = mongoose.model("Category", categorySchema)
--------------------------------------------------------------------------------
/Server/Routers/favorite.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const { getAllFavoriteItems,addItemToFavoritelist,removeItemFromFavoritelist } = require('../Controllers/favorite');
3 | const { getAccessToRoute } = require('../Middlewares/Authorization/auth')
4 |
5 | const router = express.Router();
6 |
7 |
8 | router.get('/all',getAccessToRoute , getAllFavoriteItems)
9 |
10 | router.post("/:id/add" ,getAccessToRoute , addItemToFavoritelist)
11 |
12 | router.delete("/:id/remove" ,getAccessToRoute , removeItemFromFavoritelist)
13 |
14 |
15 | module.exports = router;
--------------------------------------------------------------------------------
/Client/src/services/AddressService.js:
--------------------------------------------------------------------------------
1 | import http from "./HttpConnection";
2 |
3 | const getAllAddresses = () => {
4 | return http.get("/address/all");
5 | };
6 |
7 | const addAddress = data => {
8 | return http.post("/address/add", data);
9 | };
10 | const editAddress = (data ,id ) => {
11 | return http.put(`/address/edit/${id}`, data);
12 | };
13 |
14 | const deleteAddress = id => {
15 | return http.delete(`/address/delete/${id}`);
16 | };
17 |
18 | export default {
19 | getAllAddresses,
20 | addAddress,
21 | editAddress,
22 | deleteAddress
23 | }
--------------------------------------------------------------------------------
/Client/src/services/BankCardService.js:
--------------------------------------------------------------------------------
1 | import http from "./HttpConnection";
2 |
3 | const getAllBankCards = () => {
4 | return http.get("/bankCards/all");
5 | };
6 | const addBankCard = data => {
7 | return http.post("/bankCards/add", data);
8 | };
9 |
10 | const editBankCard = (id ,data ) => {
11 | return http.put(`/bankCards/edit/${id}`, data);
12 | };
13 |
14 | const deleteBankCard = id => {
15 | return http.delete(`/bankCards/delete/${id}`);
16 | };
17 |
18 | export default {
19 | getAllBankCards,
20 | editBankCard,
21 | deleteBankCard,
22 | addBankCard
23 | }
--------------------------------------------------------------------------------
/Server/Routers/category.js:
--------------------------------------------------------------------------------
1 |
2 | const express = require('express');
3 | const {addCategory,getAllCategories,getAllSubCategories,addSubCategory} = require('../Controllers/category');
4 | const imageUpload = require('../Helpers/Libraries/imageUpload');
5 | const router = express.Router();
6 |
7 |
8 | router.get('/all' , getAllCategories)
9 |
10 | router.post("/add" , imageUpload.single("category_image") ,addCategory)
11 |
12 | router.get('/allSubCategories' , getAllSubCategories)
13 |
14 | router.post("/:categoryId/add" , imageUpload.single("subCategory_image") ,addSubCategory)
15 |
16 |
17 | module.exports = router;
--------------------------------------------------------------------------------
/Server/Models/banner.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose')
2 |
3 | const Schema = mongoose.Schema;
4 |
5 | const bannerSchema = new Schema({
6 |
7 | name : {
8 | type : String,
9 | required : [true, "Please provide a name "],
10 | trim : true,
11 | unique : true
12 | },
13 | image : {
14 | type : String,
15 | },
16 | shippingTime : {
17 | type : Number,
18 | default : 0
19 | },
20 | deliveryTime : {
21 | type : Number,
22 | default : 0
23 | }
24 | })
25 |
26 |
27 | module.exports = mongoose.model("Banner", bannerSchema)
--------------------------------------------------------------------------------
/Client/src/view/components/general/ToastMessage.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { View, Text } from 'react-native'
3 | const ToastMessage = ({ text1 ,bgColor}) => {
4 | return (
5 |
13 |
16 | {text1}
17 |
18 |
19 | )
20 | }
21 |
22 | export default ToastMessage
23 |
--------------------------------------------------------------------------------
/Server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "1.0.0",
4 | "description": "HIZLI SEPET - Backend",
5 | "main": "server.js",
6 | "scripts": {
7 | "start": "nodemon server.js",
8 | "dev": "nodemon server.js "
9 | },
10 | "author": "Muhammet Yıldız",
11 | "license": "ISC",
12 | "dependencies": {
13 | "bcryptjs": "^2.4.3",
14 | "dotenv": "^16.0.3",
15 | "express": "^4.18.2",
16 | "express-async-handler": "^1.2.0",
17 | "jsonwebtoken": "^8.5.1",
18 | "mongoose": "^6.7.3",
19 | "multer": "^1.4.5-lts.1",
20 | "uuid": "^9.0.0"
21 | },
22 | "devDependencies": {
23 | "nodemon": "^2.0.20"
24 | }
25 | }
--------------------------------------------------------------------------------
/Server/Routers/order.js:
--------------------------------------------------------------------------------
1 |
2 | const express = require('express');
3 | const { completeOrder ,getAllMyOrders,getOrderDetails ,getAllEvaluatableItems, getAllApprovedEvaluatableItems} = require('../Controllers/order');
4 | const { getAccessToRoute } = require('../Middlewares/Authorization/auth')
5 |
6 | const router = express.Router();
7 |
8 | router.use(getAccessToRoute)
9 |
10 |
11 | router.post('/completeOrder' , completeOrder)
12 |
13 | router.get("/getAllMyOrders" , getAllMyOrders)
14 |
15 | router.get("/detail/:id" , getOrderDetails)
16 |
17 | router.get("/getAllEvaluatableItems" , getAllEvaluatableItems)
18 |
19 | router.get("/getAllApprovedEvaluatableItems" , getAllApprovedEvaluatableItems)
20 |
21 |
22 | module.exports = router;
--------------------------------------------------------------------------------
/Client/src/view/components/general/ValidationModal.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ConfirmDialog } from 'react-native-simple-dialogs'
3 |
4 | const ValidationModal = ({message,visibility ,close}) => {
5 | return (
6 | {
15 | close()
16 | },
17 | titleStyle: { color: 'green' },
18 | }}
19 | />
20 | )
21 | }
22 |
23 | export default ValidationModal
--------------------------------------------------------------------------------
/Client/src/services/OrderService.js:
--------------------------------------------------------------------------------
1 | import http from "./HttpConnection";
2 |
3 |
4 |
5 | const getAllEvaluatableItems = () => {
6 | return http.get(`/orders/getAllEvaluatableItems`);
7 | }
8 |
9 | const getAllApprovedEvaluatableItems = () => {
10 | return http.get(`/orders/getAllApprovedEvaluatableItems`);
11 | }
12 |
13 | const completeOrder = (data) => {
14 | return http.post(`/orders/completeOrder`,data);
15 | }
16 |
17 | const getAllMyOrders = () => {
18 | return http.get(`/orders/getAllMyOrders`);
19 | }
20 |
21 | const getOrderDetail = (id) => {
22 | return http.get(`/orders/detail/${id}`);
23 | }
24 |
25 | export default {
26 | getAllEvaluatableItems,
27 | getAllApprovedEvaluatableItems,
28 | completeOrder,
29 | getAllMyOrders,
30 | getOrderDetail
31 | }
--------------------------------------------------------------------------------
/Server/Routers/store.js:
--------------------------------------------------------------------------------
1 |
2 | const express = require('express');
3 | const { getAllBaskettems ,addToBasket ,increaseQuantity,decreaseQuantity,deleteAllBasketItems ,deleteBasketItem ,getAllDeliveryItems} = require('../Controllers/store');
4 | const { getAccessToRoute } = require('../Middlewares/Authorization/auth')
5 |
6 | const router = express.Router();
7 |
8 | router.use(getAccessToRoute)
9 |
10 |
11 | router.post("/addToBasket" , addToBasket)
12 |
13 | router.get('/getAllBasketItems',getAllBaskettems)
14 |
15 | router.post('/increaseQuantity' , increaseQuantity)
16 |
17 | router.post('/decreaseQuantity' , decreaseQuantity)
18 |
19 | router.delete('/deleteAllBasketItems' , deleteAllBasketItems)
20 |
21 | router.delete('/deleteBasketItem/:itemId' , deleteBasketItem)
22 |
23 | router.get('/getAllDeliveryItems',getAllDeliveryItems)
24 |
25 |
26 | module.exports = router;
--------------------------------------------------------------------------------
/Server/Models/evaluation.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose')
2 |
3 | const Schema = mongoose.Schema;
4 |
5 | const evaluationSchema = new Schema({
6 | user : {
7 | type : mongoose.Schema.ObjectId ,
8 | ref : "User",
9 | },
10 | orderItem : {
11 | type : mongoose.Schema.ObjectId ,
12 | ref : "OrderItem",
13 | required : [true, "OrderItem must belong to an order"]
14 | },
15 | product : {
16 | type : mongoose.Schema.ObjectId ,
17 | ref : "Product",
18 | required : [true, "Product must belong to an order"]
19 | },
20 | comment : {
21 | type : mongoose.Schema.ObjectId ,
22 | ref : "Comment",
23 | },
24 | appravolStatus : {
25 | type : Boolean,
26 | default : false
27 | }
28 |
29 | })
30 |
31 |
32 | module.exports = mongoose.model("Evaluation", evaluationSchema)
--------------------------------------------------------------------------------
/Server/Models/orderitem.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose')
2 |
3 | const Schema = mongoose.Schema;
4 |
5 | const orderItemSchema = new Schema({
6 | product : {
7 | type : mongoose.Schema.ObjectId ,
8 | ref : "Product",
9 | required : [true, "Order must belong to a product"]
10 | },
11 | order : {
12 | type : mongoose.Schema.ObjectId ,
13 | ref : "Order",
14 | required : [true, "Order must belong to an order"]
15 | },
16 | quantity : {
17 | type : Number,
18 | default : 0
19 | },
20 | selectedSize : {
21 | type : String,
22 | },
23 | date_added : {
24 | type : Date,
25 | default : Date.now
26 | },
27 | orderStatus : {
28 | type : Boolean,
29 | default : false
30 | },
31 |
32 | })
33 |
34 |
35 | module.exports = mongoose.model("OrderItem", orderItemSchema)
--------------------------------------------------------------------------------
/Server/Models/comment.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose')
2 |
3 | const Schema = mongoose.Schema;
4 |
5 | const commentSchema = new Schema({
6 |
7 | user : {
8 | type : mongoose.Schema.ObjectId ,
9 | ref : "User",
10 | },
11 | orderItem : {
12 | type : mongoose.Schema.ObjectId ,
13 | ref : "OrderItem",
14 | },
15 | content : {
16 | type : String,
17 | required : [true, "Please provide a content "],
18 | trim : true,
19 | },
20 | nameVisible : {
21 | type : Boolean,
22 | default : false
23 | },
24 | rating : {
25 | type : Number,
26 | default : 0
27 | },
28 | image : {
29 | type : String,
30 | },
31 | created_at : {
32 | type : Date,
33 | default : Date.now
34 | },
35 |
36 | })
37 |
38 |
39 | module.exports = mongoose.model("Comment", commentSchema)
--------------------------------------------------------------------------------
/Server/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const dotenv = require("dotenv")
3 | const app = express() ;
4 |
5 | app.use(express.json())
6 |
7 | const routers = require("./Routers/index")
8 |
9 | const path =require('path')
10 |
11 | const {customErrorHandler} = require("./Middlewares/Errors/customErrorHandler")
12 |
13 |
14 | dotenv.config({
15 | path : './Config/env/config.env'
16 | })
17 |
18 |
19 | const connectDatabase = require("./Helpers/database/connectDatabase")
20 |
21 | connectDatabase()
22 |
23 |
24 | const PORT = process.env.PORT
25 |
26 | // Routers Middleware
27 |
28 | app.use("/api" ,routers )
29 |
30 |
31 | // Error Handler
32 |
33 | app.use(customErrorHandler)
34 |
35 |
36 | // Static Files
37 | app.use(express.static(path.join(__dirname , "public") ))
38 |
39 | app.listen(PORT, () => {
40 |
41 | console.log(`App started on ${PORT} : ${process.env.NODE_ENV}`)
42 | })
--------------------------------------------------------------------------------
/Server/Controllers/banner.js:
--------------------------------------------------------------------------------
1 | const asyncErrorWrapper = require("express-async-handler");
2 | const Banner = require("../Models/banner")
3 |
4 | const getAllBanners = asyncErrorWrapper(async (req, res, next) => {
5 |
6 | const banners = await Banner.find();
7 |
8 | return res.status(200).json({
9 | success: true,
10 | banners
11 | });
12 | }
13 | );
14 |
15 | const addBanner = asyncErrorWrapper(async (req, res, next) => {
16 |
17 | const {bannerName , shippingTime ,deliveryTime } = req.body;
18 |
19 | const newBanner = await Banner.create({
20 | name : bannerName,
21 | shippingTime,
22 | deliveryTime,
23 | image : req.savedImage
24 | });
25 |
26 | return res.status(200).json({
27 | success: true,
28 | newBanner
29 | });
30 | });
31 |
32 |
33 | module.exports = {
34 | addBanner ,
35 | getAllBanners,
36 | }
--------------------------------------------------------------------------------
/Client/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "Client",
4 | "slug": "Client",
5 | "version": "1.0.0",
6 | "orientation": "portrait",
7 | "icon": "./assets/icon.png",
8 | "userInterfaceStyle": "light",
9 | "splash": {
10 | "image": "./assets/splash.png",
11 | "resizeMode": "contain",
12 | "backgroundColor": "#ffffff"
13 | },
14 | "updates": {
15 | "fallbackToCacheTimeout": 0
16 | },
17 | "assetBundlePatterns": [
18 | "**/*"
19 | ],
20 | "ios": {
21 | "supportsTablet": true
22 | },
23 | "android": {
24 | "adaptiveIcon": {
25 | "foregroundImage": "./assets/adaptive-icon.png",
26 | "backgroundColor": "#FFFFFF"
27 | }
28 | },
29 | "web": {
30 | "favicon": "./assets/favicon.png"
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/Client/src/services/HttpConnection.js:
--------------------------------------------------------------------------------
1 | import AsyncStorage from "@react-native-async-storage/async-storage";
2 | import axios from "axios";
3 | import { NGROK_URL } from '@env'
4 |
5 | const axiosInstance = axios.create({
6 | baseURL: `${NGROK_URL}` + "/api",
7 | headers: {
8 | "Content-type": "application/json",
9 | "Access-Control-Allow-Origin": "*",
10 | "Cache-Control": "no-cache"
11 | }
12 | });
13 |
14 | axiosInstance.interceptors.request.use(
15 | async (config) => {
16 | config.headers['Authorization'] = `Bearer ${JSON.parse(await AsyncStorage.getItem("accessToken"))
17 | }`;
18 | return config;
19 | },
20 | error => {
21 | console.log("error", error)
22 | return Promise.reject(error);
23 | }
24 | );
25 |
26 |
27 | axiosInstance.interceptors.response.use((response) => {
28 | return response;
29 |
30 | }, (error) => {
31 | return Promise.reject(error.response);
32 | });
33 | export default axiosInstance;
--------------------------------------------------------------------------------
/Server/Middlewares/Errors/customErrorHandler.js:
--------------------------------------------------------------------------------
1 | const CustomError = require('../../Helpers/error/CustomError')
2 |
3 | const customErrorHandler = (err, req, res, next) => {
4 |
5 | console.log("Custom Error Handler ", err.name)
6 |
7 | if (err.name === 'SyntaxError') {
8 |
9 | err = new CustomError('Unexpected Sytax ', 400)
10 | }
11 | if (err.name === 'ValidationError') {
12 |
13 | err = new CustomError(err.message, 400)
14 |
15 | }
16 | if (err.code === 11000) {
17 |
18 | err = new CustomError("Please enter different values for unique field", 400)
19 | }
20 |
21 | if (err.name === "CastError") {
22 |
23 | err = new CustomError("Please provide a valid id ", 400)
24 |
25 | }
26 |
27 | console.log(err.name, err.message, err.status)
28 |
29 | return res.status(err.status || 500).json({
30 | success: false,
31 | message: err.message
32 | })
33 |
34 | }
35 |
36 |
37 | module.exports = {
38 | customErrorHandler
39 | };
--------------------------------------------------------------------------------
/Server/Routers/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const auth = require('./auth')
3 | const user = require('./user')
4 | const product = require('./product')
5 | const favorite = require('./favorite')
6 | const store = require('./store')
7 | const order = require('./order')
8 | const address = require('./address')
9 | const bankCard = require('./bankCard')
10 | const banner = require('./banner')
11 | const comment = require('./comment')
12 | const category = require('./category')
13 |
14 | const router = express.Router()
15 |
16 | router.use('/auth' ,auth)
17 |
18 | router.use('/user' ,user)
19 |
20 | router.use('/products' ,product)
21 |
22 | router.use('/favorites' ,favorite)
23 |
24 | router.use('/store' ,store)
25 |
26 | router.use('/orders' ,order)
27 |
28 | router.use('/address' ,address)
29 |
30 | router.use('/bankCards' ,bankCard)
31 |
32 | router.use('/banners' ,banner)
33 |
34 | router.use('/comments' ,comment)
35 |
36 | router.use('/categories' ,category)
37 |
38 |
39 | module.exports = router
--------------------------------------------------------------------------------
/Client/src/services/StoreService.js:
--------------------------------------------------------------------------------
1 | import http from "./HttpConnection";
2 |
3 | const addToBasket = (data) => {
4 | return http.post(`/store/addToBasket`,data);
5 | }
6 |
7 | const getAllBasketItems = () => {
8 | return http.get(`/store/getAllBasketItems`);
9 | }
10 |
11 | const increaseQuanity = (data) => {
12 | return http.post(`/store/increaseQuantity`,data)
13 |
14 | }
15 | const decreaseQuanity = (data) => {
16 | return http.post(`/store/decreaseQuantity`,data)
17 | }
18 |
19 | const deleteBasketItem = (id) => {
20 | return http.delete(`/store/deleteBasketItem/${id}`);
21 | }
22 |
23 | const deleteAllBasketItems = () => {
24 | return http.delete(`/store/deleteAllBasketItems`);
25 | }
26 |
27 | const getAllDeliveryItems = () => {
28 | return http.get(`/store/getAllDeliveryItems`);
29 | }
30 |
31 | export default {
32 | addToBasket,
33 | getAllBasketItems,
34 | increaseQuanity,
35 | decreaseQuanity,
36 | deleteBasketItem,
37 | deleteAllBasketItems,
38 | getAllDeliveryItems,
39 | }
--------------------------------------------------------------------------------
/Client/src/services/ProductService.js:
--------------------------------------------------------------------------------
1 | import http from "./HttpConnection";
2 |
3 | const getAllProducts = () => {
4 | return http.get("/products/all");
5 | };
6 | const getDetailProduct = (id) => {
7 | return http.get(`/products/${id}/detail`);
8 | };
9 |
10 | const lastViewedProducts = () => {
11 | return http.get("/products/lastViewed/all");
12 | };
13 |
14 | const getProductsInSameSubCategory = (id) => {
15 | return http.get(`/products/${id}/inSameSubCategory`);
16 | };
17 |
18 | const getRecommendationsForItemsInBasket = () => {
19 | return http.get("/products/recommendationsForItemsInBasket");
20 | };
21 |
22 | const getSuggestedSearchWords = () => {
23 | return http.get("/products/suggestedSearchWords/all");
24 | };
25 |
26 | const searchProduct = (queryObj) => {
27 | return http.post("/products/search",queryObj);
28 | };
29 | export default {
30 | getAllProducts,
31 | getDetailProduct,
32 | lastViewedProducts,
33 | getProductsInSameSubCategory,
34 | getRecommendationsForItemsInBasket,
35 | getSuggestedSearchWords,
36 | searchProduct
37 | }
--------------------------------------------------------------------------------
/Server/Helpers/Libraries/imageUpload.js:
--------------------------------------------------------------------------------
1 |
2 | const multer = require('multer')
3 | const path = require('path')
4 | const CustomError = require('../error/CustomError')
5 |
6 | const storage = multer.diskStorage({
7 |
8 | destination : function(req, file ,cb) {
9 |
10 | const rootDir = path.dirname(require.main.filename) ;
11 |
12 | cb(null , path.join(rootDir , "/public/uploads"))
13 | },
14 | filename : function(req,file,cb ) {
15 |
16 | const extensions = file.mimetype.split("/")[1] ;
17 |
18 | req.savedImage = "image_" + Date.now() + "." + extensions
19 |
20 | cb(null ,req.savedImage)
21 |
22 | }
23 |
24 | })
25 |
26 | const fileFilter = (req,file ,cb ) => {
27 |
28 | allowedMimeTypes = ["image/jpg" ,"image/jpeg","image/png","image/gif"]
29 |
30 | if (! allowedMimeTypes.includes(file.mimetype)) {
31 |
32 | return cb(new CustomError("Please provide a valid image file",400),false);
33 |
34 | }
35 |
36 | return cb(null ,true )
37 |
38 | }
39 |
40 | const imageUpload = multer({storage ,fileFilter})
41 |
42 | module.exports = imageUpload ;
--------------------------------------------------------------------------------
/Server/Middlewares/Authorization/auth.js:
--------------------------------------------------------------------------------
1 | const asyncErrorWrapper = require("express-async-handler");
2 | const CustomError = require('../../Helpers/error/CustomError')
3 | const jwt = require("jsonwebtoken")
4 | const { isTokenIncluded, getAccessTokenFromHeader } = require('../../Helpers/authorization/tokenHelpers')
5 |
6 |
7 | const getAccessToRoute = asyncErrorWrapper((req, res, next) => {
8 | const { JWT_SECRET_KEY } = process.env
9 |
10 | if (!isTokenIncluded(req)) {
11 |
12 | return next(new CustomError("You are not authorized to access this route ", 401))
13 |
14 | }
15 |
16 | const accessToken = getAccessTokenFromHeader(req)
17 |
18 | jwt.verify(accessToken, JWT_SECRET_KEY, (err, decoded) => {
19 |
20 | if (err) {
21 | return next(new CustomError("You are not authorized to access this route", 401))
22 | }
23 |
24 | req.user = {
25 | id: decoded.id,
26 | name: decoded.name,
27 | email: decoded.email
28 | }
29 |
30 | })
31 |
32 | next();
33 |
34 | })
35 |
36 |
37 | module.exports = {
38 | getAccessToRoute
39 | }
--------------------------------------------------------------------------------
/Client/src/Context/CategoryContext.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext, useEffect } from 'react'
2 | import CategoryService from '../services/CategoryService';
3 |
4 | export const CategoryContext = React.createContext();
5 |
6 | const CategoryContextProvider = ({ children }) => {
7 | const [allCategories, setAllCategories] = useState([]);
8 |
9 | const getAllCategories = async () => {
10 | try {
11 |
12 | const { data } = await CategoryService.getAllCategories();
13 |
14 | setAllCategories(data.categories);
15 | }
16 | catch (err) {
17 | console.log("get all categories error :", err)
18 | setAllCategories([]);
19 |
20 | }
21 |
22 | }
23 |
24 | useEffect(() => {
25 | getAllCategories()
26 | }, [])
27 |
28 |
29 | return (
30 |
33 | {children}
34 |
35 | )
36 |
37 | }
38 |
39 |
40 | export const useCategories = () => {
41 | return useContext(CategoryContext)
42 | }
43 |
44 | export default CategoryContextProvider
--------------------------------------------------------------------------------
/Server/Routers/product.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const { addProduct,addImageForProduct ,getAllProducts ,getDetailProduct ,editProduct,lastViewedProducts, getProductsInSameSubCategory ,getRecommendationsForItemsInBasket,getSuggestedSearchWords ,searchProduct } = require('../Controllers/product');
3 | const { getAccessToRoute } = require('../Middlewares/Authorization/auth')
4 |
5 | const imageUpload = require('../Helpers/Libraries/imageUpload');
6 |
7 | const router = express.Router();
8 |
9 |
10 | router.get('/all',getAllProducts)
11 |
12 | router.post("/add" , addProduct)
13 |
14 | router.get("/:id/detail" , getAccessToRoute,getDetailProduct)
15 |
16 | router.post("/:id/addImage" , [imageUpload.single("product_image")]
17 | ,addImageForProduct)
18 |
19 | router.put("/edit/:id" ,editProduct)
20 |
21 | router.get("/lastViewed/all", getAccessToRoute , lastViewedProducts)
22 |
23 | router.get("/:id/inSameSubCategory", getProductsInSameSubCategory)
24 |
25 | router.get("/recommendationsForItemsInBasket", getAccessToRoute, getRecommendationsForItemsInBasket)
26 |
27 | router.get("/suggestedSearchWords/all", getSuggestedSearchWords)
28 |
29 | router.post('/search',searchProduct)
30 |
31 |
32 | module.exports = router;
--------------------------------------------------------------------------------
/Server/Helpers/authorization/tokenHelpers.js:
--------------------------------------------------------------------------------
1 | const generateJwtFromUser = require('../../Models/user')
2 |
3 | const sendJwtToClient = (user, res) => {
4 |
5 | const token = user.generateJwtFromUser();
6 |
7 | const { NODE_ENV } = process.env;
8 |
9 | return res
10 | .status(200)
11 | .cookie("access_token", token, {
12 | httpOnly: true,
13 | secure: NODE_ENV === "development" ? false : true
14 | })
15 | .json({
16 | success: true,
17 | access_token: token,
18 | data: {
19 | id: user._id,
20 | email: user.email,
21 | username: user.username,
22 | }
23 | })
24 |
25 |
26 | }
27 |
28 | const isTokenIncluded = (req) => {
29 | return (
30 | req.headers.authorization && req.headers.authorization.startsWith("Bearer")
31 | )
32 | }
33 |
34 |
35 | const getAccessTokenFromHeader = (req) => {
36 |
37 | const authorization = req.headers.authorization
38 |
39 | const access_token = authorization.substr(7).trim()
40 |
41 | return access_token
42 | }
43 |
44 |
45 | module.exports = { sendJwtToClient, isTokenIncluded, getAccessTokenFromHeader };
--------------------------------------------------------------------------------
/Server/Models/address.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose')
2 |
3 | const Schema = mongoose.Schema;
4 |
5 | const addressSchema = new Schema({
6 |
7 | user: {
8 | type: mongoose.Schema.ObjectId,
9 | ref: "User",
10 | required: [true, "Order must belong to a user"]
11 | },
12 | title: {
13 | type: String,
14 | required: [true, "Please provide a title"],
15 | trim: true,
16 | },
17 | name: {
18 | type: String,
19 | required: [true, "Please provide a user name"],
20 | },
21 | surname: {
22 | type: String,
23 | required: [true, "Please provide a user surname"],
24 | },
25 | phone: {
26 | type: String,
27 | required: [true, "Please provide a phone"],
28 | },
29 | city: {
30 | type: String,
31 | required: [true, "Please provide a city"],
32 | },
33 | district: {
34 | type: String,
35 | required: [true, "Please provide a district"],
36 | },
37 | neighborhood: {
38 | type: String,
39 | required: [true, "Please provide a neighborhood"],
40 | },
41 | detail: {
42 | type: String,
43 | required: [true, "Please provide a explanation"],
44 | },
45 | timestamp : {
46 | type : Date,
47 | default : Date.now
48 | }
49 |
50 | })
51 |
52 |
53 | module.exports = mongoose.model("Address", addressSchema)
--------------------------------------------------------------------------------
/Server/Models/bankCard.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose')
2 |
3 | const Schema = mongoose.Schema;
4 |
5 | const bankCardSchema = new Schema({
6 |
7 | user: {
8 | type: mongoose.Schema.ObjectId,
9 | ref: "User",
10 | required: [true, "Order must belong to a user"]
11 | },
12 | number: {
13 | type: String,
14 | required: [true, "Please provide a number"],
15 | unique: true,
16 | trim: true,
17 | maxLength: [16, "Please maximum 16 characters"],
18 | },
19 | name: {
20 | type: String,
21 | required: [true, "Please provide a name"],
22 | trim: true,
23 | },
24 | cvv: {
25 | type: String,
26 | required: [true, "Please provide a cvv"],
27 | trim: true,
28 | maxLength: [3, "Please maximum 3 characters"],
29 | },
30 | expiredMonth: {
31 | type: String,
32 | required: [true, "Please provide a expiredMonth"],
33 | trim: true,
34 | maxLength: [2, "Please maximum 2 characters"],
35 | },
36 | expiredYear: {
37 | type: String,
38 | required: [true, "Please provide a expiredYear"],
39 | maxLength: [4, "Please maximum 2 characters"],
40 | trim: true,
41 | },
42 | createdAt: {
43 | type: Date,
44 | default: Date.now
45 | },
46 |
47 | })
48 |
49 |
50 | module.exports = mongoose.model("BankCard", bankCardSchema)
--------------------------------------------------------------------------------
/Server/Models/product.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose')
2 |
3 | const Schema = mongoose.Schema;
4 |
5 | const productSchema = new Schema({
6 | code: {
7 | type: String,
8 | required: [true, "Please provide a code "],
9 | trim: true,
10 | },
11 | name: {
12 | type: String,
13 | required: [true, "Please provide a name "],
14 | trim: true,
15 | },
16 | price: {
17 | type: Number,
18 | default: 0,
19 | },
20 | sizes: [],
21 | about: [],
22 | properties: {},
23 | images: [{
24 | type: String,
25 | }],
26 | likes: [{
27 | type: mongoose.Schema.ObjectId,
28 | ref: "User"
29 | }],
30 | likeCount: {
31 | type: Number,
32 | default: 0
33 | },
34 |
35 | seller: {
36 | type: 'String',
37 | required: [true, "Please provide a seller "],
38 | },
39 | banner: {
40 | type: mongoose.Schema.ObjectId,
41 | ref: "Banner",
42 | },
43 | comments: [{
44 | type: mongoose.Schema.ObjectId,
45 | ref: "Comment"
46 | }],
47 | averageRating: {
48 | type: Number,
49 | default: 0
50 | },
51 | subCategory: {
52 | type: mongoose.Schema.ObjectId,
53 | ref: "SubCategory",
54 | },
55 | createdAt: {
56 | type: Date,
57 | default: Date.now
58 | },
59 |
60 | })
61 |
62 |
63 | module.exports = mongoose.model("Product", productSchema)
--------------------------------------------------------------------------------
/Server/Controllers/user.js:
--------------------------------------------------------------------------------
1 | const CustomError = require("../Helpers/error/CustomError")
2 | const asyncErrorWrapper = require("express-async-handler");
3 | const { comparePassword } = require('../Helpers/input/inputHelpers')
4 | const User = require("../Models/user")
5 |
6 | const changeEmail = asyncErrorWrapper(async (req, res, next) => {
7 |
8 | const { newEmail } = req.body;
9 |
10 | const isUser = await User.findOne({ email: newEmail })
11 |
12 | if (isUser) {
13 | return next(new CustomError("Bu email daha önce kullanılmış", 400))
14 | }
15 | const user = await User.findById(req.user.id)
16 |
17 | user.email = newEmail
18 |
19 | await user.save()
20 |
21 | return res.status(200).json({
22 | success: true,
23 | message: "Email başarıyla değiştirildi",
24 | })
25 | })
26 |
27 |
28 | const changePassword = asyncErrorWrapper(async (req, res, next) => {
29 | const { currentPass, newPass } = req.body;
30 |
31 | const user = await User.findById(req.user.id).select("+password")
32 |
33 | if (!comparePassword(currentPass.value, user.password)) {
34 |
35 | return next(new CustomError('Mevcut şifrenizi yanlış girdiniz.', 400))
36 |
37 | }
38 |
39 | user.password = newPass.value
40 | await user.save()
41 |
42 | return res.status(200).json({
43 | success: true,
44 | message: "Şifre başarıyla değiştirildi",
45 | })
46 |
47 | })
48 |
49 |
50 | module.exports = {
51 | changeEmail,
52 | changePassword
53 | }
--------------------------------------------------------------------------------
/Client/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import AuthContextProvider from './src/Context/AuthContext';
3 | import Navigation from './src/Navigation';
4 | import ProductContextProvider from './src/Context/ProductContext';
5 | import { MenuProvider } from 'react-native-popup-menu';
6 | import StoreContextProvider from './src/Context/StoreContext';
7 | import FavoriteContextProvider from './src/Context/FavoriteContext';
8 | import CategoryContextProvider from './src/Context/CategoryContext';
9 | import Toast from 'react-native-toast-message';
10 | import ToastMessage from './src/view/components/general/ToastMessage';
11 | import COLORS from './src/consts/colors';
12 | const toastConfig = {
13 | success : ({ text1, props }) => (
14 |
15 | ),
16 | customToast: ({ text1, props }) => (
17 |
18 | )
19 | };
20 | const App = () => {
21 | return (
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | );
37 | }
38 |
39 | export default App
40 |
--------------------------------------------------------------------------------
/Client/src/Context/AuthContext.js:
--------------------------------------------------------------------------------
1 | import AsyncStorage from '@react-native-async-storage/async-storage';
2 | import React, { useState, useContext, useEffect } from 'react'
3 |
4 | export const AuthContext = React.createContext();
5 |
6 | const AuthContextProvider = ({ children }) => {
7 | const [userInfo, setUserInfo] = useState({});
8 | const [splashLoading, setSplashLoading] = useState(false);
9 |
10 | const logout = async () => {
11 | await AsyncStorage.removeItem('userInfo');
12 | await AsyncStorage.removeItem('accessToken');
13 | setUserInfo({});
14 | }
15 |
16 | const isLoggedIn = async () => {
17 | setSplashLoading(true);
18 | try {
19 |
20 | let userInfo = await AsyncStorage.getItem('userInfo');
21 | userInfo = JSON.parse(userInfo);
22 | if (userInfo) {
23 | setUserInfo(userInfo);
24 | }
25 |
26 | }
27 | catch (err) {
28 | console.log("isLoggedIn error :", err)
29 | }
30 | setSplashLoading(false);
31 | }
32 |
33 | useEffect(() => {
34 | isLoggedIn();
35 | }, [])
36 |
37 | return (
38 |
46 | {children}
47 |
48 | )
49 |
50 | }
51 |
52 | export const AuthState = () => {
53 | return useContext(AuthContext)
54 | }
55 |
56 | export default AuthContextProvider
--------------------------------------------------------------------------------
/Client/src/view/screens/EditCommentScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import CommentService from '../../services/CommentService'
3 | import CommentPostForm from '../components/general/CommentPostForm'
4 | import Toast from 'react-native-toast-message'
5 |
6 | const EditCommentScreen = ({ route,navigation }) => {
7 |
8 | const { item } = route.params;
9 | const [disabled , setDisabled] = useState(false)
10 |
11 | const editComment = async (content,rating,nameVisible) => {
12 | setDisabled(true)
13 | try {
14 | await CommentService.editComment({
15 | content,
16 | rating,
17 | nameVisible,
18 | orderItemId: item.orderItem,
19 | productId : item.product._id
20 | })
21 | navigation.navigate('MyEvaluations')
22 | }
23 | catch (err) {
24 | Toast.show({
25 | type: 'customToast',
26 | position: 'bottom',
27 | text1: 'İstek iletilirken bir sorun oluştu.Tekrar deneyiniz.',
28 | visibilityTime: 2000,
29 | autoHide: true,
30 | bottomOffset: 0,
31 | });
32 | setTimeout(() => {
33 | setDisabled(false)
34 | } , 2000);
35 | }
36 | }
37 |
38 | return (
39 |
45 | )
46 | }
47 |
48 | export default EditCommentScreen
--------------------------------------------------------------------------------
/Client/src/view/components/productDetail/ProductBadges.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { View, StyleSheet, Text } from 'react-native'
3 | import COLORS from '../../../consts/colors'
4 |
5 | const ProductBadges = ({ properties }) => {
6 | return (
7 |
8 |
9 | {properties && Object.keys(properties).slice(0, 3).map((key, index) => (
10 |
11 | {key}
12 |
15 | {properties[key]}
16 |
17 |
18 | ))}
19 |
20 | )
21 | }
22 |
23 | const styles = StyleSheet.create({
24 | container : {
25 | flexDirection: 'row',
26 | alignItems: 'center',
27 | marginVertical: 10,
28 | borderBottomColor: '#f1f1f1',
29 | borderBottomWidth: 1,
30 | paddingBottom: 15,
31 | },
32 | badgeItem: {
33 | marginRight: 15,
34 | backgroundColor : 'rgba(0,183,97,0.07)',
35 | paddingVertical: 4,
36 | borderRadius : 5,
37 | paddingHorizontal: 7,
38 | },
39 | badgeTitle : {
40 | fontSize: 9,
41 | fontWeight: 'bold',
42 | color: COLORS.green,
43 | },
44 | badgeContent : {
45 | fontSize: 10,
46 | fontWeight: 'bold',
47 | color:'#78787a',
48 | marginTop: 3.7,
49 | }
50 | })
51 |
52 | export default ProductBadges
--------------------------------------------------------------------------------
/Client/src/view/screens/AddCommentScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import CommentService from '../../services/CommentService'
3 | import CommentPostForm from '../components/general/CommentPostForm'
4 | import Toast from 'react-native-toast-message'
5 |
6 | const AddCommentScreen = ({ route,navigation }) => {
7 |
8 | const { item } = route.params;
9 | const [disabled , setDisabled] = useState(false)
10 |
11 | const addComment = async (content,rating,nameVisible) => {
12 | setDisabled(true)
13 | try {
14 | await CommentService.addComment({
15 | content,
16 | rating,
17 | nameVisible,
18 | orderItemId: item.orderItem._id,
19 | productId : item.product._id
20 | })
21 |
22 | navigation.navigate('MyEvaluations')
23 | }
24 | catch (err) {
25 | Toast.show({
26 | type: 'customToast',
27 | position: 'bottom',
28 | text1: 'İstek iletilirken bir sorun oluştu.Tekrar deneyiniz.',
29 | visibilityTime: 2000,
30 | autoHide: true,
31 | bottomOffset: 0,
32 | });
33 | setTimeout(() => {
34 | setDisabled(false)
35 | } , 2000);
36 | }
37 |
38 | }
39 |
40 | return (
41 |
47 | )
48 | }
49 |
50 | export default AddCommentScreen
--------------------------------------------------------------------------------
/Client/src/view/screens/HomeScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import { StyleSheet, FlatList } from 'react-native'
3 | import COLORS from '../../consts/colors'
4 | import ProductCard from '../components/general/ProductCard'
5 | import { ProductsState } from '../../Context/ProductContext'
6 | import { SafeAreaView } from 'react-native-safe-area-context'
7 | import SearchSection from '../components/general/SearchSection'
8 |
9 | const HomeScreen = ({ navigation }) => {
10 |
11 | const { allProducts, getAllProducts } = ProductsState();
12 |
13 | useEffect(() => {
14 | const onfocus = navigation.addListener('focus', () => {
15 | getAllProducts()
16 | }
17 | );
18 | return onfocus;
19 | }, [navigation]);
20 |
21 |
22 | return (
23 |
24 |
27 |
28 | index}
36 | numColumns={2}
37 | columnWrapperStyle={{ justifyContent: 'space-between' }}
38 | renderItem={({ item }) => (
39 |
40 | )}
41 | />
42 |
43 |
44 | )
45 | }
46 |
47 |
48 | const styles = StyleSheet.create({
49 | container: {
50 | height: '100%',
51 | backgroundColor: COLORS.white,
52 | },
53 |
54 | })
55 |
56 | export default HomeScreen
--------------------------------------------------------------------------------
/Client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "1.0.0",
4 | "main": "node_modules/expo/AppEntry.js",
5 | "scripts": {
6 | "start": "expo start",
7 | "android": "expo start --android",
8 | "ios": "expo start --ios",
9 | "web": "expo start --web"
10 | },
11 | "dependencies": {
12 | "@react-native-async-storage/async-storage": "^1.17.11",
13 | "@react-navigation/bottom-tabs": "^6.4.1",
14 | "@react-navigation/native": "^6.0.14",
15 | "@react-navigation/native-stack": "^6.9.2",
16 | "axios": "^1.3.2",
17 | "expo": "~47.0.8",
18 | "expo-status-bar": "~1.4.2",
19 | "react": "18.1.0",
20 | "react-native": "0.70.5",
21 | "react-native-animated-loader": "^1.0.0",
22 | "react-native-bouncy-checkbox": "^3.0.7",
23 | "react-native-element-textinput": "^2.2.0",
24 | "react-native-gesture-bottom-sheet": "^1.1.0",
25 | "react-native-indicators": "^0.17.0",
26 | "react-native-modal": "^13.0.1",
27 | "react-native-popup-menu": "^0.16.1",
28 | "react-native-progress": "^5.0.0",
29 | "react-native-ratings": "^8.1.0",
30 | "react-native-safe-area-context": "^4.4.1",
31 | "react-native-screens": "^3.18.2",
32 | "react-native-simple-dialogs": "^1.5.0",
33 | "react-native-step-indicator": "^1.0.3",
34 | "react-native-svg": "13.4.0",
35 | "react-native-toast-message": "^2.1.6"
36 | },
37 | "devDependencies": {
38 | "@babel/core": "^7.12.9",
39 | "react-native-dotenv": "^3.4.7"
40 | },
41 | "private": true
42 | }
43 |
--------------------------------------------------------------------------------
/Server/Controllers/address.js:
--------------------------------------------------------------------------------
1 | const CustomError = require("../Helpers/error/CustomError");
2 | const asyncErrorWrapper = require("express-async-handler");
3 | const Address = require("../Models/address")
4 |
5 | const getAllAddresses = asyncErrorWrapper(async (req, res, next) => {
6 |
7 | const addresses = await Address.find({ user: req.user.id });
8 |
9 | return res.status(200).json({
10 | success: true,
11 | addresses
12 | });
13 |
14 | });
15 |
16 |
17 |
18 | const addAddress = asyncErrorWrapper(async (req, res, next) => {
19 |
20 | const { address } = req.body;
21 |
22 | if (!address) {
23 | return next(new CustomError("Please provide an address", 400));
24 | }
25 |
26 | const newAddress = await Address.create({
27 | ...address,
28 | user: req.user.id
29 | });
30 |
31 | return res.status(200).json({
32 | success: true,
33 | newAddress
34 | });
35 | });
36 |
37 |
38 | const editAddress = asyncErrorWrapper(async (req, res, next) => {
39 |
40 | const { address, id } = req.body;
41 |
42 | if (!address) {
43 | return next(new CustomError("Please provide an address", 400));
44 | }
45 |
46 | const editedAddress = await Address.findByIdAndUpdate(id, {
47 | ...address,
48 | user: req.user.id
49 | });
50 |
51 | return res.status(200).json({
52 | success: true,
53 | editedAddress
54 | });
55 | });
56 |
57 |
58 | const deleteAddress = asyncErrorWrapper(async (req, res, next) => {
59 |
60 | const { id } = req.params;
61 |
62 | if (!id) {
63 | return next(new CustomError("Please provide an id", 400));
64 | }
65 |
66 | await Address.findByIdAndDelete(id);
67 |
68 | return res.status(200).json({
69 | success: true,
70 | message: "address deleted",
71 | });
72 | });
73 |
74 | module.exports = {
75 | getAllAddresses,
76 | addAddress,
77 | editAddress,
78 | deleteAddress
79 | }
--------------------------------------------------------------------------------
/Server/Controllers/favorite.js:
--------------------------------------------------------------------------------
1 | const asyncErrorWrapper = require("express-async-handler");
2 | const User = require("../Models/user");
3 | const Product = require("../Models/product");
4 |
5 | const getAllFavoriteItems = asyncErrorWrapper(async (req, res, next) => {
6 | const user = await User.findById(req.user.id)
7 | .select('')
8 | .populate({
9 | path: "likedProducts",
10 | select: "name price about images sizes seller averageRating comments"
11 | })
12 |
13 | return res.status(200).json({
14 | success: true,
15 | favorites: user.likedProducts
16 | })
17 |
18 | })
19 |
20 | const addItemToFavoritelist = asyncErrorWrapper(async (req, res, next) => {
21 | const { id } = req.params;
22 | const product = await Product.findById(id);
23 | const activeUser = await User.findById(req.user.id);
24 |
25 | product.likes.unshift(req.user.id);
26 | product.likeCount = product.likes.length;
27 | activeUser.likedProducts.unshift(id);
28 |
29 | await activeUser.save();
30 | await product.save();
31 |
32 | return res.status(200).json({
33 | success: true,
34 | product
35 | });
36 | });
37 |
38 |
39 | const removeItemFromFavoritelist = asyncErrorWrapper(async (req, res, next) => {
40 | const { id } = req.params;
41 | const product = await Product.findById(id);
42 | const activeUser = await User.findById(req.user.id);
43 |
44 | const index = product.likes.indexOf(req.user.id);
45 | product.likes.splice(index, 1);
46 | product.likeCount = product.likes.length;
47 | activeUser.likedProducts.splice(activeUser.likedProducts.indexOf(id), 1);
48 | await activeUser.save();
49 | await product.save();
50 |
51 | return res.status(200).json({
52 | success: true,
53 | message: "item removed from favorite list"
54 | });
55 |
56 | })
57 |
58 |
59 |
60 | module.exports = {
61 | getAllFavoriteItems,
62 | addItemToFavoritelist,
63 | removeItemFromFavoritelist
64 | }
--------------------------------------------------------------------------------
/Server/Controllers/category.js:
--------------------------------------------------------------------------------
1 | const CustomError = require("../Helpers/error/CustomError");
2 | const asyncErrorWrapper = require("express-async-handler");
3 | const Category = require("../Models/category");
4 | const Subcategory = require("../Models/subcategory");
5 |
6 | const getAllCategories = asyncErrorWrapper(async (req, res, next) => {
7 |
8 | const categories = await Category.find().populate("subCategories");
9 |
10 | return res.status(200).json({
11 | success: true,
12 | categories
13 | });
14 | });
15 |
16 | const getAllSubCategories = asyncErrorWrapper(async (req, res, next) => {
17 |
18 | const subCategories = await Subcategory.find().populate("category");
19 |
20 | return res.status(200).json({
21 | success: true,
22 | subCategories
23 | });
24 |
25 | });
26 |
27 | const addSubCategory = asyncErrorWrapper(async (req, res, next) => {
28 |
29 | const {name } = req.body;
30 |
31 | const {categoryId} = req.params;
32 |
33 | const category = await Category.findById(categoryId);
34 |
35 | if(!category){
36 | return next(new CustomError("There is no such category with that id",400));
37 | }
38 |
39 | const subCategory = await Subcategory.create({
40 | name,
41 | image : req.savedImage,
42 | category : categoryId
43 | });
44 |
45 |
46 | category.subCategories.push(subCategory.id);
47 |
48 | await category.save();
49 |
50 | return res.status(200).json({
51 | success: true,
52 | subCategory
53 | });
54 |
55 | });
56 |
57 |
58 |
59 | const addCategory = asyncErrorWrapper(async (req, res, next) => {
60 |
61 | const {name} = req.body;
62 |
63 | const category = await Category.create({
64 | name,
65 | image : req.savedImage
66 | });
67 |
68 | return res.status(200).json({
69 | success: true,
70 | category
71 | });
72 | });
73 |
74 |
75 | module.exports = {
76 | addCategory,
77 | getAllCategories,
78 | getAllSubCategories,
79 | addSubCategory
80 | }
--------------------------------------------------------------------------------
/Client/src/Context/FavoriteContext.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext, useEffect } from 'react'
2 | import FavoriteService from '../services/FavoriteService';
3 | import { AuthState } from './AuthContext';
4 |
5 | export const FavoriteContext = React.createContext();
6 |
7 | const FavoriteContextProvider = ({ children }) => {
8 | const { userInfo } = AuthState()
9 | const [favoriteList, setFavoriteList] = useState([]);
10 | const [loading, setLoading] = useState(false);
11 | const [favoriteListStatus, setFavoriteListStatus] = useState(false);
12 |
13 | const getAllFavoriteItems = async () => {
14 | const { data } = await FavoriteService.getAllFavoriteItems()
15 | setFavoriteList(data.favorites)
16 | setLoading(false)
17 | }
18 |
19 | const addItemToFavoriteList = async (id) => {
20 | setLoading(true)
21 |
22 | await FavoriteService.addItemToFavoritelist(id);
23 |
24 | setFavoriteListStatus(!favoriteListStatus)
25 |
26 | }
27 |
28 | const removeItemFromFavoriteList = async (id) => {
29 |
30 | setLoading(true)
31 | try {
32 |
33 | await FavoriteService.removeItemFromFavoritelist(id);
34 | setFavoriteListStatus(!favoriteListStatus)
35 | }
36 | catch (error) {
37 | console.log(error)
38 | setLoading(false)
39 | }
40 | }
41 |
42 | useEffect(() => {
43 | getAllFavoriteItems()
44 | }, [favoriteListStatus, userInfo])
45 |
46 |
47 |
48 | return (
49 |
59 | {children}
60 |
61 | )
62 |
63 | }
64 |
65 |
66 | export const FavoriteState = () => {
67 | return useContext(FavoriteContext)
68 | }
69 |
70 | export default FavoriteContextProvider
--------------------------------------------------------------------------------
/Server/Controllers/bankCard.js:
--------------------------------------------------------------------------------
1 | const CustomError = require("../Helpers/error/CustomError");
2 | const asyncErrorWrapper = require("express-async-handler");
3 | const BankCard = require("../Models/bankCard")
4 |
5 | const getAllBankCards = asyncErrorWrapper(async (req, res, next) => {
6 |
7 | const bankCards = await BankCard.find({user: req.user.id});
8 |
9 | return res.status(200).json({
10 | success: true,
11 | bankCards
12 | });
13 | }
14 | );
15 |
16 | const addBankCard = asyncErrorWrapper(async (req, res, next) => {
17 |
18 | const {number,cvv,expiredMonth ,expiredYear } = req.body;
19 |
20 | const cardType = number.at(0) === "4" && "Visa" || number.at(0) === "5" && "MasterCard" || number.at(0) === "3" && "American Express" || "1.";
21 |
22 | if(!number) {
23 | return next(new CustomError("Please provide an number", 400));
24 | }
25 |
26 | const newBankCard = await BankCard.create({
27 | number ,
28 | cvv,
29 | expiredMonth ,
30 | expiredYear,
31 | name : cardType + ' Kartım',
32 | user: req.user.id
33 | });
34 |
35 | return res.status(200).json({
36 | success: true,
37 | newBankCard
38 | });
39 |
40 | });
41 |
42 |
43 | const editBankCard = asyncErrorWrapper(async (req, res, next) => {
44 |
45 | const { name } = req.body;
46 | const {id} = req.params;
47 |
48 | const editedBankCard = await BankCard.findByIdAndUpdate(id, {
49 | name
50 | });
51 |
52 | return res.status(200).json({
53 | success: true,
54 | editedBankCard
55 | });
56 |
57 | });
58 |
59 |
60 | const deleteBankCard = asyncErrorWrapper(async (req, res, next) => {
61 |
62 | const { id } = req.params;
63 |
64 | const deletedBankCard = await BankCard.findByIdAndDelete(id);
65 |
66 | return res.status(200).json({
67 | success: true,
68 | deletedBankCard
69 | });
70 |
71 | });
72 |
73 |
74 | module.exports = {
75 | getAllBankCards,
76 | editBankCard,
77 | deleteBankCard,
78 | addBankCard
79 | }
--------------------------------------------------------------------------------
/Server/Models/order.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const Orderitem = require('./orderitem');
3 | const Evaluation = require('./evaluation');
4 |
5 | const Schema = mongoose.Schema;
6 |
7 | const orderSchema = new Schema({
8 |
9 | user : {
10 | type : mongoose.Schema.ObjectId ,
11 | ref : "User",
12 | required : [true, "Order must belong to a user"]
13 | },
14 | complete : {
15 | type : Boolean,
16 | default : false
17 | },
18 | transaction_id : {
19 | type : String,
20 | required : [true, "Please provide a transaction id"],
21 | unique : true ,
22 | trim : true ,
23 | },
24 | dateOrdered : {
25 | type : Date,
26 | default : Date.now
27 | },
28 | orderAddress : {
29 | type : mongoose.Schema.ObjectId,
30 | ref : "Address",
31 | },
32 | orderPayment : {
33 | type : mongoose.Schema.ObjectId,
34 | ref : "BankCard",
35 | },
36 | }
37 | )
38 |
39 | orderSchema.post("save", async function (next) {
40 |
41 | if(this.complete) {
42 |
43 | const orderItems = await Orderitem.find(({ order: this._id, orderStatus: true }))
44 |
45 | const filteredOrderItems = orderItems.filter((orderItem, index, self) =>
46 | index === self.findIndex((t) => (
47 | t.product._id.toString() === orderItem.product._id.toString()
48 | ))
49 | )
50 |
51 | filteredOrderItems.forEach(async (orderItem) =>{
52 |
53 | const evaluation = await Evaluation.findOne({
54 | user : this.user._id ,
55 | product : orderItem.product._id
56 | })
57 |
58 | if(!evaluation) {
59 | await Evaluation.create({
60 | user : this.user._id ,
61 | orderItem : orderItem._id,
62 | product : orderItem.product._id,
63 | })
64 | }
65 | })
66 | }
67 | })
68 |
69 |
70 | module.exports = mongoose.model("Order", orderSchema)
--------------------------------------------------------------------------------
/Client/src/Context/ProductContext.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext, useEffect } from 'react'
2 | import ProductService from '../services/ProductService';
3 | import { MONTHS } from '../consts/time';
4 |
5 | export const ProductContext = React.createContext();
6 |
7 | const ProductContextProvider = ({ children }) => {
8 | const [allProducts, setAllProducts] = useState([]);
9 | const [suggestedList, setSuggestedList] = useState([]);
10 |
11 | const getAllProducts = async () => {
12 | try {
13 | const { data } = await ProductService.getAllProducts();
14 | setAllProducts(data.products);
15 | }
16 | catch (err) {
17 | setAllProducts([]);
18 | }
19 | }
20 |
21 | const getSuggestedSearchWords = async () => {
22 |
23 | try {
24 | const { data } = await ProductService.getSuggestedSearchWords();
25 | setSuggestedList(data.allOptions);
26 | }
27 | catch (err) {
28 | setSuggestedList([]);
29 | }
30 | }
31 |
32 |
33 |
34 | useEffect(() => {
35 | getAllProducts()
36 | getSuggestedSearchWords()
37 | }, [])
38 |
39 | const editTimestamp = (timestamp, addDay = 0, status = null) => {
40 | const date = new Date(timestamp);
41 | date.setTime(date.getTime() + (addDay * 24 * 60 * 60 * 1000))
42 |
43 | const localDate = new Date(date);
44 | const month = MONTHS[localDate.getMonth()][0].toUpperCase() + MONTHS[localDate.getMonth()].toLowerCase().substring(1);
45 |
46 | if (status === 'exactDate') {
47 | return `${localDate.getDate()} ${month} `
48 | }
49 | return `${localDate.getDate()} - ${localDate.getDate() + 1} ${month} `
50 | }
51 |
52 | return (
53 |
61 | {children}
62 |
63 | )
64 |
65 | }
66 |
67 |
68 | export const ProductsState = () => {
69 | return useContext(ProductContext)
70 | }
71 |
72 | export default ProductContextProvider
--------------------------------------------------------------------------------
/Client/src/view/components/general/ModalMessage.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Modal from "react-native-modal";
3 | import { ActivityIndicator, Text, View } from 'react-native'
4 | import { AuthState } from '../../../Context/AuthContext';
5 | import COLORS from '../../../consts/colors';
6 |
7 | const ModalMessage = ({message}) => {
8 | const { isModalVisible, setModalVisible, isLoading } = AuthState();
9 |
10 | return (
11 |
12 | setModalVisible(false)}
14 | animationIn="bounceIn"
15 | animationOut="bounceOut"
16 | backdropOpacity={0.5}
17 | animationInTiming={650}
18 | animationOutTiming={350}
19 | >
20 |
28 | {!isLoading ?
29 |
35 |
41 | {message}
42 |
43 | setModalVisible(false)}
45 | >
46 | OKEY
47 |
48 |
49 | :
50 |
58 |
61 |
62 | Yükleniyor
63 |
64 |
65 | }
66 |
67 |
68 |
69 |
70 | )
71 | }
72 |
73 | export default ModalMessage
--------------------------------------------------------------------------------
/Server/Controllers/auth.js:
--------------------------------------------------------------------------------
1 | const CustomError = require("../Helpers/error/CustomError")
2 | const { sendJwtToClient } = require('../Helpers/authorization/tokenHelpers')
3 | const { validateUserInput, comparePassword } = require('../Helpers/input/inputHelpers')
4 | const asyncErrorWrapper = require("express-async-handler")
5 | const User = require("../Models/user")
6 |
7 | const register = asyncErrorWrapper(async (req, res, next) => {
8 |
9 | const { username, email, password, role } = req.body
10 |
11 | const isUser = await User.findOne({ email })
12 |
13 | if (isUser) {
14 | return next(new CustomError("Bu email kullanılamaz. Lütfen başka bir email deneyiniz.", 400))
15 | }
16 |
17 | const newUser = await User.create({
18 | username,
19 | email,
20 | password,
21 | role
22 | })
23 |
24 | sendJwtToClient(newUser, res)
25 |
26 | })
27 |
28 |
29 | const login = asyncErrorWrapper(async (req, res, next) => {
30 |
31 | const { email, password } = req.body;
32 |
33 | if (!validateUserInput(email, password)) {
34 |
35 | return next(new CustomError("Please check your inputs ", 400))
36 | }
37 |
38 | const user = await User.findOne({ email }).select("+password")
39 |
40 | if (!user) {
41 | return next(new CustomError('Invalid credentials ', 400));
42 | }
43 |
44 | if (!comparePassword(password, user.password)) {
45 |
46 | return next(new CustomError('Please check your credentails ', 400))
47 |
48 | }
49 |
50 | sendJwtToClient(user, res)
51 |
52 | })
53 |
54 | const getActiveUser = asyncErrorWrapper(async (req, res, next) => {
55 |
56 | const user = await User.findById(req.user.id)
57 | .select("email username ")
58 | .populate("likedProducts", "name price")
59 |
60 | return res.status(200).json({
61 | success: true,
62 | user
63 | })
64 |
65 | })
66 |
67 |
68 | const logout = asyncErrorWrapper(async (req, res, next) => {
69 |
70 | const { NODE_ENV } = process.env;
71 |
72 | return res.status(200)
73 | .cookie({
74 | httpOnly: true,
75 | expires: new Date(Date.now()),
76 | secure: NODE_ENV === "development" ? false : true
77 | }).json({
78 | success: true,
79 | message: "Logout Successfull"
80 | })
81 |
82 | })
83 | module.exports = {
84 | register,
85 | login,
86 | getActiveUser,
87 | logout
88 | }
--------------------------------------------------------------------------------
/Client/src/view/components/productDetail/ImageSlider.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { SafeAreaView, View, Text, StyleSheet, Image, Dimensions, ScrollView } from 'react-native';
3 | import {NGROK_URL} from '@env'
4 |
5 | const WIDTH = Dimensions.get('window').width;
6 | const HEIGHT = Dimensions.get('window').height;
7 |
8 | const ImageSlider = ({images}) => {
9 |
10 | const [imgActive, setImgActive] = useState(0)
11 | const onChange = (nativeEvent) => {
12 | if(nativeEvent){
13 | const slide = Math.floor(nativeEvent.contentOffset.x / nativeEvent.layoutMeasurement.width)
14 | if (slide !== imgActive) {
15 | setImgActive(slide)
16 | }
17 | }
18 | }
19 |
20 | return (
21 |
22 |
23 | onChange(nativeEvent)}
25 | showsHorizontalScrollIndicator={false}
26 | pagingEnabled
27 | horizontal
28 | style={styles.wrap}
29 | >
30 | { images?.map((image, index) => (
31 |
37 |
38 | ))}
39 |
40 |
41 |
42 | {images?.map((e, index) => (
43 |
47 | ⬤
48 |
49 |
50 | ))}
51 |
52 |
53 |
54 |
55 |
56 | )
57 | }
58 |
59 | const styles = StyleSheet.create({
60 | container: {
61 | height :'100%',
62 | },
63 | wrap: {
64 | width: WIDTH,
65 | height: HEIGHT *0.64,
66 | },
67 | wrapDot: {
68 | backgroundColor: 'rgba(0,0,0,0.7)',
69 | paddingHorizontal: 10,
70 | borderRadius: 10,
71 | position: 'absolute',
72 | bottom: 0,
73 | display: 'flex',
74 | flexDirection: 'row',
75 | alignSelf: 'center',
76 | alignItems: 'center',
77 | justifyContent: 'center'
78 | },
79 | dotActive: {
80 | margin: 3,
81 | color: "white",
82 | fontSize: 11
83 | },
84 | dot: {
85 | margin: 3,
86 | color: '#dedede',
87 | fontSize: 10
88 | }
89 | })
90 |
91 | export default ImageSlider
--------------------------------------------------------------------------------
/Client/src/view/screens/AddAddressScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import AddressPostForm from '../components/address/AddressPostForm'
3 | import AddressService from '../../services/AddressService'
4 | import Toast from 'react-native-toast-message'
5 |
6 | const AddAddressScreen = ({ navigation ,route}) => {
7 |
8 | const fields = ["title", "name", "surname", "phone", "detail", "neighborhood", "city", "district"]
9 | const [address, setAddress] = useState(
10 | fields.reduce((acc, field) => {
11 | acc[field] = {
12 | value: '',
13 | error: {
14 | message: '',
15 | status: false
16 | },
17 | isFocus: false
18 | }
19 | return acc
20 | }, {})
21 | )
22 | const [disabled , setDisabled] = useState(false)
23 |
24 | const onSubmit = async (address) => {
25 | setDisabled(true)
26 | let newAddress = {
27 | title: address.title.value,
28 | name: address.name.value,
29 | surname: address.surname.value,
30 | phone: address.phone.value,
31 | detail: address.detail.value,
32 | neighborhood: address.neighborhood.value,
33 | city: address.city.value,
34 | district: address.district.value
35 | }
36 | try {
37 | await AddressService.addAddress( {address:newAddress})
38 |
39 | if(route?.params?.prevRouteName === "Checkout"){
40 | navigation.navigate('Checkout', { address: newAddress })
41 | }
42 | else {
43 | navigation.navigate('MyAddresses')
44 | }
45 | }
46 | catch (error) {
47 | Toast.show({
48 | type: 'customToast',
49 | position: 'bottom',
50 | text1: 'İstek iletilirken bir sorun oluştu.Tekrar deneyiniz.',
51 | visibilityTime: 2000,
52 | autoHide: true,
53 | bottomOffset: 0,
54 | });
55 | setTimeout(() => {
56 | setDisabled(false)
57 | } , 2000);
58 | }
59 | }
60 |
61 | return (
62 |
69 | )
70 | }
71 |
72 | export default AddAddressScreen
73 |
--------------------------------------------------------------------------------
/Client/src/view/components/general/CardImageSlider.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { SafeAreaView, View, Text, StyleSheet, Image, ScrollView } from 'react-native';
3 | import {NGROK_URL} from '@env'
4 |
5 | const CardImageSlider = ({images}) => {
6 | const [imgActive, setImgActive] = useState(0)
7 |
8 | const onChange = (nativeEvent) => {
9 | if(nativeEvent){
10 | const slide = Math.floor(nativeEvent.contentOffset.x / nativeEvent.layoutMeasurement.width)
11 | if (slide !== imgActive) {
12 | setImgActive(slide)
13 | }
14 |
15 | }
16 | }
17 |
18 | return (
19 |
20 |
21 | onChange(nativeEvent)}
23 | showsHorizontalScrollIndicator={false}
24 | pagingEnabled
25 | horizontal
26 | style={styles.wrap}
27 | >
28 | { images?.map((image, index) => (
29 |
37 | ))}
38 |
39 |
40 |
41 | {images?.map((e, index) => (
42 |
46 | ⬤
47 |
48 |
49 | ))}
50 |
51 |
52 |
53 |
54 |
55 | )
56 | }
57 |
58 | const styles = StyleSheet.create({
59 | container: {
60 | height :'100%',
61 | width: 155,
62 | flexDirection: 'row',
63 | justifyContent: 'center',
64 | alignItems: 'center',
65 | zIndex: 10,
66 | },
67 | wrap: {
68 | height: '100%',
69 | width: 150,
70 | },
71 | wrapDot: {
72 | backgroundColor: 'rgba(0,0,0,0.6)',
73 | paddingHorizontal: 5,
74 | borderRadius: 10,
75 | position: 'absolute',
76 | bottom: 5,
77 | display: 'flex',
78 | flexDirection: 'row',
79 | alignSelf: 'center',
80 | alignItems: 'center',
81 | justifyContent: 'center'
82 |
83 | },
84 | dotActive: {
85 | margin: 2.5,
86 | color: "white",
87 | fontSize: 6
88 | },
89 | dot: {
90 | margin: 2.5,
91 | color: '#dedede',
92 | fontSize: 6
93 | }
94 | })
95 |
96 |
97 | export default CardImageSlider
--------------------------------------------------------------------------------
/Client/src/view/components/productDetail/SizeOptionsSection.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { View, StyleSheet, Text, ScrollView,TouchableOpacity } from 'react-native'
3 | import COLORS from '../../../consts/colors'
4 |
5 | const SizeOptionsSection = ({product,selectedSize,setSelectedSize}) => {
6 | return (
7 |
8 |
9 | {product.subCategory.name === 'Ayakkabı' && 'Ayakkabı Numarası' ||
10 | product.subCategory.name === 'Telefon' && 'Dahili Hafıza' ||
11 | product.subCategory.name === 'Tablet' && 'Kapasite' ||
12 | 'Beden'
13 | }
14 |
15 |
23 | {product?.sizes?.map((size, index) => (
24 | setSelectedSize(size)}
27 | >
28 |
29 | {size}
30 |
31 |
32 | ))}
33 |
34 |
35 |
36 | )
37 | }
38 |
39 | const styles = StyleSheet.create({
40 | chooseSizeSection: {
41 | height: 125,
42 | backgroundColor: COLORS.white,
43 | marginTop: 20,
44 | marginBottom: 0,
45 | paddingHorizontal: 12,
46 | elevation: 2,
47 |
48 | },
49 | sizeTxt: {
50 | fontSize: 14, fontWeight: '500', paddingVertical: 13, color: '#2C2E43'
51 | , borderBottomColor: '#eee', borderBottomWidth: 1
52 | },
53 | sizeBtn: {
54 | paddingHorizontal: 10,
55 | borderRadius: 5,
56 | marginVertical: 10,
57 | marginRight: 14,
58 | borderWidth: 1,
59 | borderColor: 'gray',
60 | height: 39,
61 | width: 'auto',
62 | minWidth: 55,
63 | flexDirection: 'row',
64 | alignItems: 'center',
65 | justifyContent: 'center',
66 | },
67 | activeSizeBtn: {
68 | backgroundColor: 'rgba(16, 176, 2,0.1 )',
69 | borderColor: COLORS.green,
70 | },
71 |
72 | })
73 |
74 | export default SizeOptionsSection
75 |
--------------------------------------------------------------------------------
/Client/src/view/components/productDetail/ColorOptionsSection.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { View, Image, StyleSheet, Text } from 'react-native'
3 | import COLORS from '../../../consts/colors'
4 | import { NGROK_URL } from '@env'
5 |
6 | const ColorOptionsSection = ({colorOptions,navigation,product}) => {
7 | return (
8 |
9 |
10 |
11 | Renk
12 |
13 |
16 | {colorOptions.length} Farklı Renk
17 |
18 |
19 |
20 | {
21 | colorOptions.map((item, index) => (
22 |
23 | {
25 | navigation.push('Details', { productId: item._id })
26 | }}
27 | style={[styles.colorOption,
28 | {
29 | borderWidth: 1,
30 | borderColor: item._id === product._id ? COLORS.green : '#dedede'
31 | }
32 |
33 | ]}
34 | key={index}
35 | >
36 |
37 |
41 |
42 | ))
43 | }
44 |
45 |
46 | )
47 | }
48 |
49 | const styles = StyleSheet.create({
50 | chooseColorSection: {
51 | height: 205,
52 | backgroundColor: COLORS.white,
53 | marginTop: 20,
54 | paddingHorizontal: 12,
55 | elevation: 2,
56 | },
57 | colorOptions: {
58 | flexDirection: 'row',
59 | alignItems: 'center',
60 | justifyContent: 'flex-start',
61 | },
62 | colorOption: {
63 | height: '80%',
64 | width: 110,
65 | borderRadius: 8,
66 | marginRight: 20,
67 | },
68 | colorOptionImage: {
69 | width: '100%',
70 | height: '100%',
71 | resizeMode: 'contain',
72 | },
73 | colorTxt: {
74 | fontSize: 14, fontWeight: '500', paddingVertical: 13, color: '#2C2E43'
75 | },
76 | headerColorSec: {
77 | borderBottomColor: '#eee', borderBottomWidth: 1,
78 | flexDirection: 'row',
79 | alignItems: 'center',
80 | justifyContent: 'space-between'
81 | },
82 |
83 | })
84 |
85 | export default ColorOptionsSection
--------------------------------------------------------------------------------
/Server/Models/user.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose')
2 | const bcrypt = require('bcryptjs');
3 | const jwt = require('jsonwebtoken')
4 | const Order = require('./order');
5 | const { v4: uuidv4 } = require('uuid');
6 | const Schema = mongoose.Schema;
7 |
8 | const userSchema = new Schema({
9 |
10 | username: {
11 | type: String,
12 | required: [true, "Please provide a username "]
13 | },
14 | email: {
15 | type: String,
16 | required: [true, 'Please provide a email '],
17 | unique: true,
18 | match: [/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/, 'Please fill a valid email address']
19 | },
20 | role: {
21 | type: String,
22 | default: "user",
23 | enum: ["user", "admin"]
24 | },
25 | password: {
26 | type: String,
27 | minlength: [6, "Please provide a password with min length : 6 "],
28 | required: [true, "Please provide a password"],
29 | select: false
30 | },
31 | createdAt: {
32 | type: Date,
33 | default: Date.now
34 | },
35 | likedProducts: [{
36 | type: mongoose.Schema.ObjectId,
37 | ref: "Product"
38 | }],
39 | about: {
40 | type: String
41 | },
42 | profile_image: {
43 | type: String,
44 | default: 'default.jpg'
45 | },
46 | blocked: {
47 | type: Boolean,
48 | default: false
49 | },
50 | resetPasswordToken: {
51 | type: String
52 | },
53 | resetPasswordExpire: {
54 | type: Date
55 | }
56 |
57 | })
58 |
59 | userSchema.methods.generateJwtFromUser = function () {
60 |
61 | const { JWT_SECRET_KEY } = process.env;
62 |
63 | const payload = {
64 | id: this._id,
65 | name: this.name,
66 | email: this.email
67 | };
68 |
69 | const token = jwt.sign(payload, JWT_SECRET_KEY);
70 |
71 | return token
72 |
73 | }
74 |
75 |
76 | userSchema.pre('save', function (next) {
77 |
78 | if (!this.isModified("password")) {
79 | next()
80 | }
81 |
82 | bcrypt.genSalt(10, (err, salt) => {
83 |
84 | if (err) next(err);
85 |
86 | bcrypt.hash(this.password, salt, (err, hash) => {
87 | if (err) next(err);
88 | this.password = hash
89 | next()
90 | });
91 | });
92 |
93 | })
94 |
95 | userSchema.post('save', async function () {
96 |
97 | const order = await Order.findOne({ user: this._id, complete: false })
98 |
99 | if (!order) {
100 |
101 | await Order.create({
102 | user: this._id,
103 | complete: false,
104 | transaction_id: uuidv4()
105 |
106 | })
107 | }
108 |
109 | })
110 |
111 |
112 | module.exports = mongoose.model("User", userSchema)
--------------------------------------------------------------------------------
/Client/src/view/screens/EditAddressScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import AddressPostForm from '../components/address/AddressPostForm'
3 | import AddressService from '../../services/AddressService'
4 | import Toast from 'react-native-toast-message'
5 |
6 | const EditAddressScreen = ({ route, navigation }) => {
7 |
8 | const fields = ["title", "name", "surname", "phone", "detail", "neighborhood", "city", "district"]
9 | const [address, setAddress] = useState({})
10 | const { prevRouteName } = route.params
11 | const [disabled, setDisabled] = useState(false)
12 |
13 | useEffect(() => {
14 | setAddress(
15 | fields.reduce((acc, field) => {
16 | acc[field] = {
17 | value: route.params.address[field],
18 | error: {
19 | message: '',
20 | status: false
21 | },
22 | isFocus: false
23 | }
24 | return acc
25 | }, {})
26 | )
27 | }, [route.params.address])
28 |
29 | const onSubmit = async (address) => {
30 | setDisabled(true)
31 | let editAddress = {
32 | title: address.title.value,
33 | name: address.name.value,
34 | surname: address.surname.value,
35 | phone: address.phone.value,
36 | detail: address.detail.value,
37 | neighborhood: address.neighborhood.value,
38 | city: address.city.value,
39 | district: address.district.value
40 | }
41 | try {
42 | await AddressService.editAddress({ address: editAddress, id: route.params.address._id })
43 |
44 | if (prevRouteName === "Checkout") {
45 | navigation.navigate('Checkout', { address: editAddress, id: route.params.address._id })
46 | }
47 | else {
48 | navigation.navigate('MyAddresses')
49 | }
50 |
51 | }
52 | catch (error) {
53 | Toast.show({
54 | type: 'customToast',
55 | position: 'bottom',
56 | text1: 'İstek iletilirken bir sorun oluştu.Tekrar deneyiniz.',
57 | visibilityTime: 2000,
58 | autoHide: true,
59 | bottomOffset: 0,
60 | });
61 | setTimeout(() => {
62 | setDisabled(false)
63 | }, 2000);
64 |
65 | }
66 |
67 | }
68 |
69 | const deleteAddress = async () => {
70 | await AddressService.deleteAddress(route.params.address._id)
71 | navigation.navigate('MyAddresses')
72 | }
73 |
74 | return (
75 |
83 | )
84 | }
85 |
86 | export default EditAddressScreen
--------------------------------------------------------------------------------
/Client/src/view/components/productDetail/ProductInfo.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { View, StyleSheet, Text, TouchableOpacity } from 'react-native'
3 | import { Octicons } from 'react-native-vector-icons'
4 | import COLORS from '../../../consts/colors'
5 |
6 | const ProductInfo = ({ list }) => {
7 | const [showMore, setShowMore] = useState(false)
8 | return (
9 |
10 |
11 | Ürün Bilgileri
12 |
13 |
14 | {list?.slice(0, 4).map((item, index) => (
15 |
16 |
17 | {item}
18 |
19 | ))}
20 |
21 |
22 |
25 | {list?.slice(4, list.length).map((item, index) => (
26 |
27 |
28 |
29 | {item}
30 |
31 | ))}
32 |
33 |
34 | {list?.length > 4 &&
35 | setShowMore(!showMore)}
37 | activeOpacity={0.8}
38 | >
39 |
40 |
41 |
42 | {showMore ? 'Daha az göster' : 'Daha fazla göster'}
43 |
44 |
45 |
46 |
47 | }
48 |
49 |
50 | )
51 | }
52 |
53 | const styles = StyleSheet.create({
54 | container: {
55 | backgroundColor: 'white',
56 | marginTop: 20,
57 | paddingHorizontal: 20,
58 | paddingBottom: 10,
59 | elevation: 2,
60 | marginBottom: 20,
61 | },
62 | flex: {
63 | display: 'flex', flexDirection: 'row', alignItems: 'center'
64 | },
65 | title: {
66 | fontSize: 14, fontWeight: '500', paddingVertical: 15, color: '#2C2E43'
67 | , borderBottomColor: '#eee', borderBottomWidth: 1
68 | },
69 |
70 | infoItem: {
71 | flexDirection: 'row',
72 | paddingVertical: 5,
73 | marginTop: 3,
74 | },
75 | showCloseBtn: {
76 | paddingVertical: 13,
77 | borderRadius: 5,
78 | alignItems: 'center',
79 | justifyContent: 'center',
80 | borderWidth: 1,
81 | borderColor: '#F2F2F2',
82 | marginTop: 10,
83 | }
84 | })
85 |
86 | export default ProductInfo
--------------------------------------------------------------------------------
/Client/src/view/components/checkout/ContractForms.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Text, View, StyleSheet, ScrollView } from 'react-native'
3 | import COLORS from '../../../consts/colors'
4 | import TEXTS from '../../../consts/text'
5 |
6 | const ContractForms = () => {
7 | return (
8 |
9 |
18 | Sözleşme ve Formlar
19 |
20 |
21 |
22 |
26 | Ön Bilgilendirme Koşulları
27 |
28 |
34 |
35 | {TEXTS.contractText}
36 |
37 |
38 |
42 | Mesafeli Satış Sözleşmesi
43 |
44 |
50 |
51 | {TEXTS.sellingContract}
52 |
53 |
54 |
55 |
56 |
57 | )
58 | }
59 |
60 | const styles = StyleSheet.create({
61 | contractForms: {
62 | backgroundColor: 'white',
63 | paddingHorizontal: 15,
64 | paddingBottom: 25,
65 | paddingTop: 15,
66 | elevation: 3,
67 | marginBottom: 85,
68 | marginTop: 20,
69 | },
70 | headText: {
71 | fontSize: 16,
72 | fontWeight: 'bold',
73 | color: 'gray',
74 | marginBottom: 15,
75 | },
76 | contractText: {
77 | fontSize: 11,
78 | color: 'rgb(153, 153, 153)',
79 | lineHeight: 17,
80 | },
81 | sellingContract: {
82 | fontSize: 11,
83 | color: 'rgb(153, 153, 153)',
84 | lineHeight: 17,
85 | },
86 | formWrap: {
87 | borderWidth: 1,
88 | overflow: 'scroll',
89 | height: 110,
90 | backgroundColor: 'rgb(246,246,246)',
91 | borderRadius: 5,
92 | paddingHorizontal: 15,
93 | paddingVertical: 10,
94 | borderColor: 'rgba(200,200,200,0.5)'
95 | },
96 |
97 | })
98 |
99 | export default ContractForms
--------------------------------------------------------------------------------
/Client/src/view/components/favorite/FavoritesMenu.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { StyleSheet, Text, TouchableOpacity } from 'react-native'
3 | import { Menu, MenuOptions, MenuOption, MenuTrigger } from 'react-native-popup-menu';
4 | import { Entypo, AntDesign, MaterialCommunityIcons } from 'react-native-vector-icons'
5 | import { FavoriteState } from '../../../Context/FavoriteContext';
6 |
7 | const FavoritesMenu = ({ productId }) => {
8 | const { removeItemFromFavoriteList } = FavoriteState();
9 |
10 | return (
11 |
72 | )
73 | }
74 |
75 | const styles = StyleSheet.create({
76 | menu: {
77 | width: 31,
78 | borderRadius: 50,
79 | marginLeft: 300,
80 | height: 33,
81 | position: 'absolute',
82 | zIndex: 5,
83 | top: 10,
84 | right: 10,
85 | },
86 | menuOption: {
87 | padding: 15,
88 | flexDirection: 'row',
89 | alignItems: 'center',
90 | },
91 | menuIcon: {
92 | marginRight: 10
93 | }
94 | })
95 | export default FavoritesMenu
--------------------------------------------------------------------------------
/Client/src/Context/StoreContext.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext, useEffect } from 'react'
2 | import StoreService from '../services/StoreService';
3 | import { AuthState } from './AuthContext';
4 | import Toast from 'react-native-toast-message';
5 |
6 | export const StoreContext = React.createContext();
7 |
8 | const StoreContextProvider = ({ children }) => {
9 | const { userInfo } = AuthState()
10 | const [basketItemsLength, setBasketItemsLength] = useState(0);
11 | const [basketItems, setBasketItems] = useState([]);
12 | const [totalPrice, setTotalPrice] = useState(0);
13 |
14 | const [loading, setLoading] = useState(false);
15 | const [basketItemStatus, setBasketItemStatus] = useState(false);
16 |
17 | const getAllBasketItems = async () => {
18 | const { data } = await StoreService.getAllBasketItems();
19 | setBasketItems(data.orderItems)
20 | setBasketItemsLength(data.orderItems.length)
21 | setTotalPrice(data.totalPrice)
22 | setLoading(false)
23 |
24 | }
25 | const addToBasket = async ({ productId, selectedSize }) => {
26 | setLoading(true)
27 |
28 | try {
29 | await StoreService.addToBasket({ productId, selectedSize });
30 |
31 | }
32 | catch (err) {
33 | Toast.show({
34 | type: 'customToast',
35 | position: 'bottom',
36 | text1: 'İstek iletilirken bir sorun oluştu.Tekrar deneyiniz.',
37 | visibilityTime: 2000,
38 | autoHide: true,
39 | bottomOffset: 0,
40 | });
41 | }
42 |
43 | setBasketItemStatus(!basketItemStatus)
44 |
45 | }
46 |
47 | const increaseQuantity = async (id) => {
48 | await StoreService.increaseQuanity({ itemId: id });
49 |
50 | }
51 | const decreaseQuantity = async (id) => {
52 | await StoreService.decreaseQuanity({ itemId: id });
53 | }
54 |
55 | const deleteBasketItem = async (id) => {
56 | setLoading(true)
57 | await StoreService.deleteBasketItem(id);
58 | setBasketItemStatus(!basketItemStatus)
59 | }
60 |
61 | const deleteAllBasketItems = async () => {
62 | setLoading(true)
63 | await StoreService.deleteAllBasketItems();
64 | setBasketItems([])
65 | setBasketItemsLength(0)
66 | setTotalPrice(0)
67 | setLoading(false)
68 | }
69 |
70 |
71 | useEffect(() => {
72 | getAllBasketItems()
73 | }, [basketItemStatus, userInfo])
74 |
75 | return (
76 |
92 | {children}
93 |
94 | )
95 |
96 | }
97 |
98 |
99 | export const StoreState = () => {
100 | return useContext(StoreContext)
101 | }
102 |
103 | export default StoreContextProvider
--------------------------------------------------------------------------------
/Client/src/view/components/productDetail/RecommendProductItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { View, StyleSheet, Text, Image } from 'react-native'
3 | import COLORS from '../../../consts/colors'
4 | import { Rating } from 'react-native-ratings'
5 | import { NGROK_URL } from '@env'
6 |
7 | const RecommendProductItem = ({ item, productId, navigation }) => {
8 | return (
9 |
14 | navigation.push('Details', { productId: item._id })}
17 | >
18 |
22 |
23 |
24 |
25 | {item.seller} {item.name.substring(0, 35)}{item.name.length > 35 && '...'}
26 |
27 |
28 |
29 |
30 |
43 |
44 | ({item?.comments?.length})
45 |
46 |
47 |
48 |
49 |
50 | {item.price} TL
51 |
52 |
53 |
54 | )
55 | }
56 | const styles = StyleSheet.create({
57 | container: {
58 | backgroundColor: COLORS.white,
59 | marginTop: 5,
60 | paddingHorizontal: 12,
61 | elevation: 2,
62 | width: '100%',
63 | marginBottom: 55,
64 | },
65 | productItem: {
66 | width: 130,
67 | marginRight: 14,
68 | height: 300,
69 | },
70 | title: {
71 | fontSize: 14, fontWeight: '500', paddingVertical: 17, color: '#2C2E43'
72 | },
73 | imageWrapper: {
74 | height: 170,
75 | borderWidth: 1,
76 | borderColor: 'rgba(0,0,0,0.09)',
77 | marginBottom: 10,
78 | borderRadius: 6,
79 | },
80 | image: {
81 | width: '100%',
82 | height: '100%',
83 | resizeMode: 'contain',
84 | },
85 | productName: {
86 | fontSize: 11,
87 | fontWeight: 'bold',
88 | color: '#8c8c8c',
89 | lineHeight: 15,
90 | paddingHorizontal: 2,
91 | marginBottom: 5,
92 | height: 37,
93 | },
94 | flex: {
95 | flexDirection: 'row',
96 | alignItems: 'center',
97 | paddingLeft: 2,
98 | },
99 | productPrice: {
100 | fontSize: 12,
101 | fontWeight: 'bold',
102 | color: COLORS.green,
103 | paddingLeft: 2,
104 | marginTop: 12,
105 | }
106 |
107 | })
108 |
109 |
110 | export default RecommendProductItem
--------------------------------------------------------------------------------
/Client/src/view/components/productDetail/RecommendProducts.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import { View, StyleSheet, Text, FlatList } from 'react-native'
3 | import COLORS from '../../../consts/colors'
4 | import ProductService from '../../../services/ProductService'
5 | import RecommendProductItem from './RecommendProductItem'
6 |
7 | const RecommendProducts = ({ productId, subCategoryId ,navigation}) => {
8 |
9 | const [products, setProducts] = useState([])
10 | const [similarProducts, setSimilarProducts] = useState([])
11 |
12 | const getProductsInSameSubCategory = async () => {
13 | try {
14 | const { data } = await ProductService.getProductsInSameSubCategory(subCategoryId);
15 | setSimilarProducts(data.products)
16 | }
17 | catch (err) {
18 | console.log("error :", err)
19 | setSimilarProducts([])
20 | }
21 |
22 | }
23 |
24 |
25 | const getLastViewedProducts = async () => {
26 | try {
27 | const { data } = await ProductService.lastViewedProducts();
28 |
29 | setProducts(data.lastVieweds.products)
30 |
31 | }
32 | catch (err) {
33 | console.log(" error :", err)
34 | setProducts([])
35 | }
36 |
37 | }
38 |
39 | useEffect(() => {
40 | getLastViewedProducts()
41 | getProductsInSameSubCategory()
42 | }, [subCategoryId])
43 |
44 |
45 |
46 | return (
47 | <>
48 | {similarProducts.length >= 2 &&
49 |
50 |
51 | Benzer Ürünler
52 |
53 |
54 | item._id}
59 | renderItem={({ item }) => (
60 |
61 | )}
62 | />
63 |
64 |
65 | }
66 | {products.length >= 3 &&
67 |
68 |
69 | Son Baktıgınız Ürünler
70 |
71 | item._id}
76 | renderItem={({ item }) => (
77 |
78 |
79 | )}
80 | />
81 |
82 |
83 | }
84 | >
85 |
86 | )
87 | }
88 | const styles = StyleSheet.create({
89 | container: {
90 | backgroundColor: COLORS.white,
91 | marginTop: 5,
92 | paddingHorizontal: 12,
93 | elevation: 2,
94 | width: '100%',
95 | marginBottom: 55,
96 | },
97 | title: {
98 | fontSize: 14, fontWeight: '500', paddingVertical: 17, color: '#2C2E43',
99 | paddingBottom:10,
100 | marginBottom: 10,
101 | },
102 |
103 | })
104 |
105 |
106 | export default RecommendProducts
--------------------------------------------------------------------------------
/Client/src/view/components/productDetail/ProductProperties.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { View, StyleSheet, Text, TouchableOpacity } from 'react-native'
3 |
4 | const ProductProperties = ({ properties }) => {
5 | const [showMore, setShowMore] = useState(false)
6 | return (
7 | properties &&
8 |
9 |
10 | Ürün Özellikleri
11 |
12 |
13 | {properties && Object.keys(properties).slice(0, 4).map((key, index) => (
14 |
15 | {key} :
16 |
19 | {properties[key]}
20 |
21 |
22 | ))}
23 |
24 |
25 |
28 | {properties && Object.keys(properties).slice(4, properties.length).map((key, index) => (
29 |
30 |
31 | {key} :
32 |
35 | {properties[key]}
36 |
37 |
38 | ))}
39 |
40 |
41 | {properties && Object.keys(properties)?.length > 2 &&
42 | setShowMore(!showMore)}
44 | activeOpacity={0.8}
45 | >
46 |
47 |
48 |
49 |
50 | {showMore ? 'Daha az göster' : 'Daha fazla göster'}
51 |
52 |
53 |
54 | }
55 |
56 |
57 |
58 | )
59 | }
60 |
61 | const styles = StyleSheet.create({
62 | container: {
63 | backgroundColor: 'white',
64 | marginTop: 0,
65 | paddingHorizontal: 20,
66 | paddingBottom: 10,
67 | elevation: 2,
68 | marginBottom: 20,
69 | },
70 | flex: {
71 | display: 'flex', flexDirection: 'row', alignItems: 'center'
72 | },
73 | title: {
74 | fontSize: 14, fontWeight: '500', paddingVertical: 15, color: '#2C2E43'
75 | , borderBottomColor: '#eee', borderBottomWidth: 1
76 | },
77 | propertyItem: {
78 | flexDirection: 'row',
79 | paddingVertical: 13,
80 | marginTop: 6,
81 | justifyContent: 'space-between',
82 | borderBottomColor: '#f5f5f5',
83 | borderBottomWidth: 1,
84 | },
85 | showCloseBtn: {
86 | paddingVertical: 13,
87 | borderRadius: 5,
88 | alignItems: 'center',
89 | justifyContent: 'center',
90 | borderWidth: 1,
91 | borderColor: '#F2F2F2',
92 | marginTop: 10,
93 | }
94 | })
95 |
96 | export default ProductProperties
--------------------------------------------------------------------------------
/Client/src/view/components/checkout/ConfirmationField.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Text, View, StyleSheet, Dimensions } from 'react-native'
3 | import COLORS from '../../../consts/colors'
4 | import BouncyCheckbox from 'react-native-bouncy-checkbox'
5 |
6 | const ConfirmationField = ({ confirmChecked, setConfirmChecked }) => {
7 | return (
8 |
9 |
23 |
28 | Ön Bilgilendirme formunu
29 |
30 |
36 | ve
37 |
38 |
41 | Mesafeli Satış Sözleşmesini
42 |
43 |
51 | onaylıyorum
52 |
53 |
54 | }
55 | iconStyle={{ borderColor: "black", marginRight: -5, }}
56 | innerIconStyle={{
57 | borderWidth: 2,
58 | borderRadius: 5,
59 | }}
60 | onPress={() =>
61 | setConfirmChecked({
62 | value: !confirmChecked.value,
63 | error: false,
64 | })
65 | }
66 | style={{
67 | marginBottom: 17,
68 | }}
69 | isChecked={confirmChecked.value}
70 | />
71 |
72 |
73 | )
74 | }
75 |
76 | const styles = StyleSheet.create({
77 | confirmationField: {
78 | height: 80,
79 | padding: 15,
80 | paddingTop: 20,
81 | flexDirection: 'row',
82 | alignItems: 'center',
83 | marginVertical: 10,
84 | },
85 | textUnderline: {
86 | textDecorationLine: 'underline',
87 | color: COLORS.green,
88 | fontWeight: 'bold',
89 | marginHorizontal: 6,
90 | fontSize: 11,
91 | letterSpacing: 0.3,
92 | },
93 | errorText: {
94 | color: '#ed071e',
95 | }
96 | })
97 |
98 | export default ConfirmationField
--------------------------------------------------------------------------------
/Server/Controllers/comment.js:
--------------------------------------------------------------------------------
1 |
2 | const asyncErrorWrapper = require("express-async-handler");
3 | const Comment = require("../Models/comment");
4 | const Product = require("../Models/product");
5 | const Evaluation = require("../Models/evaluation");
6 |
7 |
8 | const addComment = asyncErrorWrapper(async (req, res, next) => {
9 |
10 | const { content, rating, nameVisible, orderItemId, productId } = req.body;
11 |
12 | const newComment = await Comment.create({
13 | orderItem: orderItemId,
14 | user: req.user.id,
15 | content,
16 | rating,
17 | nameVisible
18 | })
19 | const product = await Product.findById(productId).populate("comments", "rating")
20 |
21 | product.comments.push(newComment)
22 |
23 | product.averageRating = (product.comments.reduce((acc, item) => item.rating + acc, 0)) / product.comments.length
24 |
25 | await product.save()
26 |
27 | const evaluationItem = await Evaluation.findOne({ user: req.user.id, product: productId })
28 |
29 | evaluationItem.appravolStatus = true
30 | evaluationItem.comment = newComment;
31 |
32 | await evaluationItem.save()
33 |
34 | return res.status(200).json({
35 | success: true,
36 | comment: newComment
37 | })
38 |
39 | })
40 |
41 | const deleteComment = asyncErrorWrapper(async (req, res, next) => {
42 |
43 | const { commentId, productId } = req.body;
44 |
45 | const product = await Product.findById(productId).populate("comments", "rating")
46 |
47 | const index = product.comments.findIndex(item => item._id.toString() === commentId)
48 |
49 | if (product.comments.length === 1) {
50 | product.averageRating = 0
51 | }
52 | else {
53 | product.averageRating = (product.comments.reduce((acc, item) => item.rating + acc, 0) - product.comments[index].rating) / (product.comments.length - 1)
54 | }
55 |
56 | product.comments.splice(index, 1)
57 |
58 | await product.save()
59 |
60 | const evaluationItem = await Evaluation.findOne({ user: req.user.id, product: productId })
61 | evaluationItem.appravolStatus = false
62 | evaluationItem.comment = null;;
63 | await evaluationItem.save()
64 |
65 | await Comment.findByIdAndDelete(commentId)
66 |
67 | return res.status(200).json({
68 | success: true,
69 | message: "Comment deleted"
70 | })
71 |
72 | })
73 |
74 |
75 |
76 | const editComment = asyncErrorWrapper(async (req, res, next) => {
77 |
78 | const { content, rating, nameVisible, orderItemId, productId } = req.body;
79 |
80 | const comment = await Comment.findOne({
81 | orderItem: orderItemId,
82 | user: req.user.id,
83 | })
84 |
85 | comment.content = content
86 |
87 | comment.rating = rating
88 |
89 | comment.nameVisible = nameVisible
90 |
91 | await comment.save()
92 |
93 | const product = await Product.findById(productId).populate("comments", "rating")
94 |
95 | product.averageRating = (product.comments.reduce((acc, item) => item.rating + acc, 0)) / product.comments.length
96 |
97 | await product.save()
98 |
99 | return res.status(200).json({
100 | success: true,
101 | comment: comment
102 | })
103 |
104 | })
105 |
106 | const getAllComments = asyncErrorWrapper(async (req, res, next) => {
107 |
108 | const comments = await Comment.find({ product: req.params.id })
109 |
110 | return res.status(200).json({
111 | success: true,
112 | comments
113 | })
114 |
115 | })
116 |
117 |
118 | module.exports = {
119 | getAllComments,
120 | addComment,
121 | editComment,
122 | deleteComment,
123 | }
--------------------------------------------------------------------------------
/Client/src/view/screens/SearchResultsScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import { View, Text, StyleSheet, ScrollView } from 'react-native'
3 | import { SafeAreaView } from 'react-native-safe-area-context';
4 | import SearchSection from '../components/general/SearchSection';
5 | import COLORS from '../../consts/colors';
6 | import { MaterialIndicator } from 'react-native-indicators'
7 | import ProductService from '../../services/ProductService';
8 | import ProductCard from '../components/general/ProductCard';
9 | import { Ionicons } from 'react-native-vector-icons'
10 |
11 | const SearchResultsScreen = ({ navigation, route }) => {
12 |
13 | const { queryObj } = route.params;
14 | const [loading, setLoading] = useState(true)
15 | const [searchResults, setSearchResults] = useState([])
16 | const [warnMsg, setWarnMsg] = useState('')
17 |
18 | const searchProduct = async () => {
19 |
20 | try {
21 | if (queryObj.type) {
22 | const { data } = await ProductService.searchProduct({ queryObj })
23 | setSearchResults(data.results)
24 | data.message ? setWarnMsg(data.message) : setWarnMsg('')
25 | setLoading(false)
26 | }
27 | }
28 | catch (error) {
29 | setSearchResults([])
30 | setLoading(false)
31 | }
32 |
33 | }
34 |
35 | useEffect(() => {
36 | setLoading(true)
37 | searchProduct()
38 | }, [queryObj])
39 |
40 |
41 | return (
42 |
45 |
49 | {loading ?
50 |
54 | :
55 |
58 | {warnMsg &&
59 |
62 |
63 | {warnMsg}
66 |
67 | }
68 |
77 | {
78 | searchResults.map((item, index) => (
79 |
82 | ))
83 | }
84 |
85 |
86 |
87 |
88 | }
89 |
90 |
91 | )
92 | }
93 | const styles = StyleSheet.create({
94 | container: {
95 | flex: 1,
96 | backgroundColor: COLORS.white,
97 |
98 | },
99 | messageWrapper : {
100 | flexDirection: 'row',
101 | alignItems: 'center',
102 | paddingHorizontal: 15,
103 | marginHorizontal: 10,
104 | marginTop: 10,
105 | paddingRight: 35,
106 | paddingVertical: 15,
107 | elevation: 5,
108 | backgroundColor: COLORS.white,
109 | }
110 | })
111 | export default SearchResultsScreen
--------------------------------------------------------------------------------
/Client/src/view/components/evaluation/EvaluationMenu.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { StyleSheet, Text, TouchableOpacity } from 'react-native'
3 | import { Menu, MenuOptions, MenuOption, MenuTrigger } from 'react-native-popup-menu';
4 | import { MaterialIcons, Entypo, MaterialCommunityIcons } from 'react-native-vector-icons'
5 | import COLORS from '../../../consts/colors';
6 | import CommentService from '../../../services/CommentService';
7 |
8 | const EvaluationMenu = ({ item, navigation, getAllData }) => {
9 |
10 | const deleteEvaluation = async () => {
11 | try {
12 | await CommentService.deleteComment({
13 | commentId: item.comment._id,
14 | productId: item.product._id
15 | })
16 | getAllData()
17 |
18 | }
19 | catch (error) {
20 | console.log("error", error)
21 | }
22 |
23 | }
24 |
25 |
26 | return (
27 |
98 | )
99 | }
100 |
101 | const styles = StyleSheet.create({
102 |
103 | menu: {
104 | width: 31,
105 | borderRadius: 50,
106 | marginLeft: 300,
107 | height: 33,
108 | position: 'absolute',
109 | zIndex: 5,
110 | top: 15,
111 | right: 5,
112 | },
113 | menuOption: {
114 | paddingVertical: 12,
115 | paddingHorizontal: 15,
116 | flexDirection: 'row',
117 | alignItems: 'center',
118 | },
119 | menuIcon: {
120 | marginRight: 10
121 | }
122 | })
123 |
124 | export default EvaluationMenu
--------------------------------------------------------------------------------
/Client/src/view/components/checkout/PaymentBottomSheet.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { View, StyleSheet, Text, TouchableOpacity, FlatList } from 'react-native'
3 | import BottomSheet from 'react-native-gesture-bottom-sheet'
4 | import { Ionicons } from 'react-native-vector-icons'
5 | import COLORS from '../../../consts/colors'
6 |
7 | const PaymentBottomSheet = ({ bottomSheet, options , selectedCard,setSelectedCard ,headText,height}) => {
8 |
9 | return (
10 |
13 |
14 |
15 |
16 |
17 |
18 |
21 | {headText}
22 |
23 | { bottomSheet.current.close() }}
25 | >
26 |
31 |
32 |
33 |
34 |
35 | item}
38 | renderItem={({ item }) => (
39 | {
49 | headText === "Ay Seçiniz" ?
50 | setSelectedCard({
51 | ...selectedCard,
52 | cardExpiredMonth : {
53 | isValid :false,
54 | value : item
55 | }
56 | })
57 | :
58 | setSelectedCard({
59 | ...selectedCard,
60 | cardExpiredYear : {
61 | isValid :false,
62 | value : item
63 | }
64 | })
65 |
66 | bottomSheet.current.close()
67 | }
68 | }
69 | >
70 |
71 | {item}
72 |
73 |
74 | )}
75 | />
76 |
77 |
78 |
79 |
80 |
81 | )
82 | }
83 | const styles = StyleSheet.create({
84 | bottomSheetContent: {
85 | backgroundColor: 'white',
86 | },
87 | bottomSheetContentHeader: {
88 | backgroundColor: 'rgba(0,0,0,0.09)',
89 | paddingVertical: 10,
90 | paddingHorizontal: 20,
91 | flexDirection: 'row',
92 | justifyContent: 'space-between',
93 | alignItems: 'center',
94 | marginBottom: 10,
95 | }
96 | })
97 |
98 | export default PaymentBottomSheet
--------------------------------------------------------------------------------
/Client/src/view/components/basket/RecommendBasketItem.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useRef } from 'react'
2 | import { View, StyleSheet, Text, Image, Dimensions, TouchableOpacity } from 'react-native'
3 | import COLORS from '../../../consts/colors'
4 | import CartBottomSheet from '../general/CartBottomSheet';
5 | import { NGROK_URL } from '@env'
6 |
7 | const RecommendBasketItem = ({ item, navigation }) => {
8 | const bottomSheet = useRef(null);
9 | const [selectedSize, setSelectedSize] = useState(item?.sizes[0])
10 |
11 | return (
12 | <>
13 |
20 |
21 |
25 |
29 |
30 |
31 |
32 |
33 |
34 | {item?.seller} {item?.name.substring(0, 65)}{item.name.length > 65 && '...'}
35 |
36 |
37 |
38 |
39 | {item?.price} TL
40 |
41 |
42 | {
45 | bottomSheet.current.show()
46 | }}
47 | >
48 |
49 | Sepete Ekle
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | >
59 | )
60 | }
61 |
62 |
63 | const styles = StyleSheet.create({
64 | recommendItem: {
65 | height: 120,
66 | flexDirection: 'row',
67 | alignItems: 'center',
68 | justifyContent: 'space-between',
69 | marginBottom: 7,
70 | borderBottomColor: '#f5f5f5',
71 | borderBottomWidth: 1,
72 | paddingBottom: 12,
73 | },
74 | imageWrapper: {
75 | width: 95,
76 | height: 95,
77 | flexDirection: 'row',
78 | alignItems: 'center',
79 | justifyContent: 'center',
80 | },
81 | image: {
82 | width: 75,
83 | height: 75,
84 | resizeMode: 'contain',
85 | },
86 | productInfoWrapper: {
87 | width: Dimensions.get('window').width - 130,
88 | paddingHorizontal: 10,
89 | height: '100%',
90 | paddingTop: 10,
91 | paddingBottom: 5,
92 | flexDirection: 'column',
93 | justifyContent: 'space-between',
94 | },
95 | productName: {
96 | fontSize: 12,
97 | fontWeight: 'bold',
98 | color: '#8c8c8c',
99 | lineHeight: 16,
100 | paddingHorizontal: 2,
101 | marginBottom: 5,
102 | height: 37,
103 | },
104 | flex: {
105 | flexDirection: 'row',
106 | alignItems: 'center',
107 | justifyContent: 'space-between',
108 | },
109 | productPrice: {
110 | fontSize: 12,
111 | fontWeight: 'bold',
112 | color: COLORS.green,
113 | paddingLeft: 2,
114 | alignSelf: 'flex-end',
115 | },
116 | addBasketText: {
117 | fontSize: 11,
118 | fontWeight: 'bold',
119 | color: COLORS.green,
120 | borderWidth: 1,
121 | borderColor: COLORS.green,
122 | paddingHorizontal: 14,
123 | paddingVertical: 4,
124 | borderRadius: 5,
125 | elevation: 1,
126 | backgroundColor: 'white',
127 | textAlign: 'center',
128 | },
129 | })
130 |
131 |
132 | export default RecommendBasketItem
--------------------------------------------------------------------------------
/Client/src/view/components/productDetail/AnimatedHeader.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { View, Text, Animated } from 'react-native'
3 | import COLORS from '../../../consts/colors'
4 | import { Ionicons, AntDesign, Feather } from 'react-native-vector-icons'
5 | import {useSafeAreaInsets } from 'react-native-safe-area-context'
6 | import { StoreState } from '../../../Context/StoreContext'
7 |
8 | const HEADER_HEIGHT = 0;
9 |
10 | const AnimatedHeader = ({ animatedValue,likeStatus,navigation,product }) => {
11 | const { basketItemsLength } = StoreState();
12 | const insets = useSafeAreaInsets();
13 |
14 | const headerHeight = animatedValue.interpolate({
15 | inputRange: [0, HEADER_HEIGHT + insets.top],
16 | outputRange: [HEADER_HEIGHT + insets.top, 55],
17 | extrapolate: 'clamp'
18 | });
19 |
20 | return (
21 |
34 |
39 | navigation.goBack()}
44 | style={{
45 | position: 'absolute',
46 | bottom: 12,
47 | left: 12
48 | }}
49 | />
50 |
58 | 0 ? 'flex' : 'none'
74 | }}
75 | >
76 | {
77 | basketItemsLength
78 | }
79 |
80 |
81 |
88 |
89 |
90 |
100 |
101 |
102 |
111 | {
112 | product?.name?.length > 35 ? product?.name.substring(0, 35) + '...' : product?.name
113 | }
114 |
115 |
116 |
117 |
118 |
119 |
120 | );
121 | };
122 |
123 | export default AnimatedHeader;
--------------------------------------------------------------------------------
/Client/src/view/screens/MyOrdersScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import { Text, View, StyleSheet, Image, ScrollView, TouchableOpacity } from 'react-native'
3 | import COLORS from '../../consts/colors';
4 | import { UIActivityIndicator } from 'react-native-indicators';
5 | import OrderCard from '../components/order/OrderCard';
6 | import OrderService from '../../services/OrderService';
7 |
8 | const MyOrdersScreen = ({ navigation }) => {
9 |
10 | const [orders, setOrders] = useState([])
11 | const [loading, setLoading] = useState(false)
12 |
13 | const getAllMyOrders = async () => {
14 | setLoading(true)
15 | try {
16 | const { data } = await OrderService.getAllMyOrders()
17 | setOrders(data.orderContent)
18 | setLoading(false)
19 | }
20 | catch (error) {
21 | setLoading(false)
22 | }
23 | }
24 |
25 | useEffect(() => {
26 | getAllMyOrders()
27 | }, [])
28 |
29 | return (
30 |
33 | {
34 | loading ?
35 |
40 | :
41 |
42 | orders.length === 0 ?
43 |
51 |
60 |
61 | Siparişiniz Bulunamadı
62 |
63 |
64 |
65 | Şu anda vermiş oldugunuz sipariş bulunmamaktadır.
66 |
67 | navigation.navigate('Home')}
77 | >
78 |
79 | Alışverişe Devam Et
80 |
81 |
82 |
83 |
84 |
85 | :
86 |
87 |
90 | {
91 | orders.map((order, index) => {
92 | return (
93 |
98 | )
99 | }
100 | )
101 | }
102 |
103 | }
104 |
105 |
106 | )
107 | }
108 |
109 | const styles = StyleSheet.create({
110 | container: {
111 | flex: 1,
112 | backgroundColor: "#fff",
113 | },
114 | textLight: {
115 | color: '#413F42',
116 | fontSize: 12,
117 | color: '#757575',
118 | marginTop: 10,
119 | },
120 | btnWrapper: {
121 | backgroundColor: COLORS.green,
122 | color: 'white',
123 | paddingVertical: 12,
124 | borderRadius: 5,
125 | fontWeight: 'bold',
126 | marginTop: 10,
127 | width: '90%',
128 | textAlign: 'center',
129 | },
130 |
131 | })
132 |
133 | export default MyOrdersScreen
--------------------------------------------------------------------------------
/Client/src/view/components/address/AddressBottomSheet.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { View, Text, StyleSheet, TouchableOpacity, FlatList } from 'react-native'
3 | import BottomSheet from 'react-native-gesture-bottom-sheet'
4 | import { Ionicons } from 'react-native-vector-icons'
5 | import COLORS from '../../../consts/colors'
6 | import { AntDesign } from '@expo/vector-icons';
7 |
8 | const AddressBottomSheet = ({ bottomSheet, addresses, setSelectedAddress, navigation }) => {
9 | return (
10 |
13 |
14 |
15 |
16 |
17 |
18 | Adres Seçiniz
19 |
20 | { bottomSheet.current.close() }}
22 | >
23 |
28 |
29 |
30 |
31 |
32 |
33 | item._id}
36 | renderItem={({ item }) => (
37 | {
48 | setSelectedAddress(item)
49 | bottomSheet.current.close()
50 | }
51 | }
52 | >
53 |
54 | {item.title}
55 |
56 |
57 | {item.neighborhood} Mah / {item.district} / {item.city}
58 |
59 |
60 | )}
61 | />
62 | {
65 | bottomSheet.current.close()
66 | navigation.navigate('AddAddress', { prevRouteName: 'Checkout' })
67 | }
68 | }
69 | >
70 |
75 |
83 | Yeni Adres Ekle
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | )
92 | }
93 |
94 | const styles = StyleSheet.create({
95 | bottomSheetContent: {
96 | backgroundColor: 'white',
97 | height: '100%',
98 | },
99 | bottomSheetContentHeader: {
100 | backgroundColor: 'rgba(0,0,0,0.06)',
101 | paddingVertical: 10,
102 | paddingHorizontal: 20,
103 | flexDirection: 'row',
104 | justifyContent: 'space-between',
105 | alignItems: 'center',
106 | marginBottom: 10,
107 | },
108 | flex: {
109 | flexDirection: 'row',
110 | alignItems: 'center',
111 | paddingVertical: 11,
112 | paddingHorizontal: 20,
113 | }
114 | })
115 |
116 | export default AddressBottomSheet
117 |
--------------------------------------------------------------------------------
/Client/src/view/screens/AccountScreen.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'
3 | import { SafeAreaView } from 'react-native-safe-area-context'
4 | import { AuthState } from '../../Context/AuthContext'
5 | import { Ionicons, Entypo, AntDesign, MaterialCommunityIcons, FontAwesome5, MaterialIcons, Feather } from 'react-native-vector-icons'
6 | import COLORS from '../../consts/colors';
7 | const options = [
8 | {
9 | title: 'Siparişlerim',
10 | icon: 'package',
11 | packageName: 'Feather',
12 | href: 'MyOrders'
13 | },
14 | {
15 | title: 'Adreslerim',
16 | packageName: 'Ionicons',
17 | icon: 'location-outline',
18 | href: 'MyAddresses'
19 | },
20 | {
21 | title: 'Kayıtlı Kartlarım',
22 | packageName: 'AntDesign',
23 | icon: 'creditcard',
24 | href: 'BankCards'
25 | },
26 | {
27 | title: 'Değerlendirmelerim',
28 | packageName: 'MaterialCommunityIcons',
29 | icon: 'comment-processing-outline',
30 | href: 'MyEvaluations'
31 | },
32 | {
33 | title: 'E-Mail Degişikligi',
34 | packageName: 'FontAwesome5',
35 | icon: 'envelope',
36 | href: 'ChangeEmail'
37 | },
38 | {
39 | title: 'Şifre Degişikligi',
40 | packageName: 'MaterialIcons',
41 | icon: 'lock-outline',
42 | href: 'ChangePassword'
43 | },
44 | , {
45 | title: 'Çıkış Yap',
46 | packageName: 'MaterialCommunityIcons',
47 | icon: 'logout',
48 | href: 'Login'
49 | }
50 |
51 | ]
52 | const AccountScreen = ({ navigation }) => {
53 |
54 | const { logout } = AuthState()
55 |
56 | return (
57 |
60 |
61 |
62 | {
64 | navigation.goBack()
65 | }}
66 | >
67 |
68 |
69 |
70 |
71 | Hesabım
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | {options.map((item, index) => {
82 | return (
83 | {
86 | if (item.title === 'Çıkış Yap') {
87 | logout()
88 | }
89 | else {
90 | navigation.navigate(item.href)
91 | }
92 | }}
93 | >
94 |
95 |
96 | {item.packageName === 'Ionicons' && ||
97 | item.packageName === 'MaterialCommunityIcons' && ||
98 | item.packageName === 'AntDesign' && ||
99 | item.packageName === 'FontAwesome5' && ||
100 | item.packageName === 'MaterialIcons' && ||
101 | item.packageName === 'Feather' &&
102 | }
103 |
106 | {item.title}
107 |
108 |
109 |
110 |
111 |
112 |
113 | )
114 |
115 | })}
116 |
117 |
118 |
119 |
120 |
121 |
122 | )
123 | }
124 |
125 | const styles = StyleSheet.create({
126 | headerSection: {
127 | padding: 18,
128 | backgroundColor: COLORS.grey,
129 | },
130 | headerInfo: {
131 | width: '100%',
132 | display: 'flex',
133 | flexDirection: 'row',
134 | justifyContent: 'space-between',
135 | alignItems: 'center',
136 | },
137 | logoutBtn: {
138 | backgroundColor: 'red',
139 | padding: 10,
140 | borderRadius: 5,
141 | margin: 50,
142 | width: 100,
143 | },
144 |
145 | text: {
146 | textAlign: 'center',
147 | marginTop: 30,
148 | fontSize: 17,
149 | fontWeight: 'bold',
150 | color: 'white',
151 | marginLeft: 20,
152 | },
153 | mainSection: {
154 | backgroundColor: 'white',
155 | height: 550,
156 | padding: 15,
157 | paddingTop: 20,
158 |
159 | },
160 | mainSectionItem: {
161 | display: 'flex',
162 | flexDirection: 'row',
163 | justifyContent: 'space-between',
164 | alignItems: 'center',
165 | padding: 12,
166 | paddingVertical: 17,
167 | borderBottomWidth: 1,
168 | borderBottomColor: 'rgba(0,0,0,0.05)',
169 | }
170 |
171 | })
172 |
173 | export default AccountScreen
--------------------------------------------------------------------------------
/Client/src/view/components/checkout/SelectAddressSection.js:
--------------------------------------------------------------------------------
1 | import React, { useRef } from 'react'
2 | import { View, Text, StyleSheet, Dimensions, TouchableOpacity } from 'react-native'
3 | import COLORS from '../../../consts/colors'
4 | import { Feather } from '@expo/vector-icons';
5 | import AddressBottomSheet from '../address/AddressBottomSheet';
6 |
7 | const SelectAddressSection = ({ addresses, selectedAddress, setSelectedAddress, navigation }) => {
8 |
9 | const bottomSheet = useRef(null)
10 |
11 | return (
12 | <>
13 |
19 |
20 |
30 | Teslimat Adresi
31 | {
35 | navigation.navigate('MyAddresses')
36 | }
37 | }
38 | >
39 | Ekle / Düzenle
45 |
46 |
47 |
48 |
49 |
50 | { bottomSheet.current.show() }
55 | }
56 | >
57 |
63 |
64 |
72 | {selectedAddress?.title ?? 'Siparişiniz için adres ekleyiniz '}
73 |
74 | {selectedAddress?.title &&
82 | {
83 | String('(' + selectedAddress.neighborhood + ' Mah. / ' + selectedAddress.district + ' / ' + selectedAddress.city + ')').substring(0, (Dimensions.get('window').width - 112 - String(selectedAddress.title).length * 7) / 7)
84 | +
85 | (String(selectedAddress.neighborhood + 'Mah. / ' + selectedAddress.district + ' / ' + selectedAddress.city).length * 7 > Dimensions.get('window').width - 112 - String(selectedAddress.title).length * 7 ? '...' : '')
86 | }
87 |
88 |
89 | }
90 |
91 | {selectedAddress?.title &&
92 |
97 | }
98 |
99 |
100 |
101 |
102 |
103 | >
104 |
105 | )
106 | }
107 |
108 | const styles = StyleSheet.create({
109 | container: {
110 | width: '100%',
111 | backgroundColor: 'white',
112 | paddingHorizontal: 15,
113 | paddingTop: 20,
114 | paddingBottom: 25,
115 | marginTop: 10,
116 | elevation: 3
117 | },
118 | headText: {
119 | fontSize: 14,
120 | fontWeight: 'bold',
121 | color: COLORS.green,
122 | marginBottom: 5,
123 | marginLeft: -5
124 | },
125 | flex: {
126 | flexDirection: 'row',
127 | alignItems: 'center',
128 | justifyContent: 'space-between',
129 | },
130 | selectAddressWrapper: {
131 | position: 'relative',
132 | flexDirection: 'row',
133 | alignItems: 'center',
134 | justifyContent: 'space-between',
135 | borderWidth: 1,
136 | borderColor: 'rgba(0,0,0,0.15)',
137 | paddingHorizontal: 10,
138 | height: 50,
139 | paddingLeft: 15,
140 | marginTop: 10,
141 | borderRadius: 5,
142 | backgroundColor: 'rgba(0,0,0,0.03)',
143 | }
144 | })
145 |
146 | export default SelectAddressSection
--------------------------------------------------------------------------------
/Client/src/view/screens/BasketScreen.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { StyleSheet, Text, View, ScrollView, TouchableOpacity, Dimensions, Image } from 'react-native'
3 | import { SafeAreaView } from 'react-native-safe-area-context'
4 | import BasketItem from '../components/basket/BasketItem'
5 | import { StoreState } from '../../Context/StoreContext'
6 | import COLORS from '../../consts/colors'
7 | import * as Progress from 'react-native-progress';
8 | import FixedStoreField from '../components/general/FixedConfirmationField'
9 | import RecommendBasketItems from '../components/basket/RecommendBasketItems'
10 |
11 | const BasketScreen = ({ navigation }) => {
12 |
13 | const { basketItems, totalPrice, loading, basketItemsLength } = StoreState()
14 |
15 | return (
16 | <>
17 |
23 |
24 |
25 |
26 |
27 |
28 | Sepetim
29 |
30 | {basketItemsLength > 0 &&
31 | <>
32 |
33 | -
34 |
35 |
36 |
37 | {basketItemsLength} ürün
38 |
39 |
40 | >
41 | }
42 |
43 |
44 |
45 |
57 |
58 |
59 |
65 |
66 |
69 |
70 | {basketItemsLength > 0 ?
71 | (
72 | basketItems.map((item, index) => (
73 |
74 |
75 |
79 |
80 | ))
81 |
82 | )
83 | :
84 |
92 |
100 |
101 | Sepetim
104 |
107 | Sepetinizde ürün bulunmamaktadır.
108 |
109 | navigation.navigate('Home')}
112 | style={{
113 | width: '90%',
114 | }}
115 | >
116 |
119 | Alışverişe Devam Et
120 |
121 |
122 |
123 |
124 |
125 | }
126 |
127 |
128 |
129 |
130 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
144 | >
145 | )
146 | }
147 |
148 | const styles = StyleSheet.create({
149 | headerInfo: {
150 | paddingHorizontal: 18,
151 | paddingVertical: 20,
152 | display: 'flex',
153 | flexDirection: 'row',
154 | justifyContent: 'space-between',
155 | backgroundColor: 'white',
156 | shadowColor: '#000',
157 | shadowOffset: {
158 | width: 0,
159 | height: 2,
160 | },
161 | shadowOpacity: 0.25,
162 | shadowRadius: 3.84,
163 | elevation: 5,
164 | zIndex: 5,
165 | },
166 | headerTxtWrap: {
167 | display: 'flex',
168 | flexDirection: 'row',
169 |
170 | },
171 | basketItems: {
172 | borderRadius: 5,
173 | display: 'flex',
174 | marginBottom: 15,
175 | },
176 | flex: {
177 | display: 'flex',
178 | flexDirection: 'row',
179 | justifyContent: 'space-between',
180 | padding: 12,
181 | alignItems: 'center',
182 | },
183 | btnWrapper: {
184 | backgroundColor: COLORS.green,
185 | color: 'white',
186 | paddingVertical: 12,
187 | borderRadius: 5,
188 | fontWeight: 'bold',
189 | marginTop: 10,
190 | width: '100%',
191 | textAlign: 'center',
192 | },
193 | textLight: {
194 | color: '#413F42',
195 | fontSize: 12,
196 | color: '#757575',
197 | marginBottom: 10,
198 | },
199 | text: {
200 | fontWeight: 'bold',
201 | fontSize: 15,
202 | marginBottom: 10,
203 | color: '#575656',
204 | },
205 |
206 | })
207 |
208 | export default BasketScreen
--------------------------------------------------------------------------------
/Client/src/view/screens/CategoriesScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { View, Text, StyleSheet, Image, FlatList, TouchableOpacity, Dimensions } from 'react-native'
3 | import { SafeAreaView } from 'react-native-safe-area-context'
4 | import { useCategories } from '../../Context/CategoryContext'
5 | import { NGROK_URL } from '@env'
6 | import COLORS from '../../consts/colors'
7 | import SearchSection from '../components/general/SearchSection'
8 |
9 | const CategoriesScreen = ({ navigation }) => {
10 |
11 | const { allCategories } = useCategories()
12 | const [activeIndex, setActiveIndex] = useState(0)
13 |
14 | return (
15 | <>
16 |
17 |
18 |
19 |
22 | item.name}
26 | renderItem={({ item, index }) => (
27 | setActiveIndex(index)}
33 | >
34 |
39 |
43 | {item.name}
44 |
45 |
46 |
47 | )}
48 | />
49 |
50 |
51 |
52 |
53 |
54 | {
55 | allCategories[activeIndex].subCategories.map((item, index) => {
56 |
57 | return (
58 | navigation.navigate('SearchResults', { queryObj: { type: 'category', name: item.name, categoryName: allCategories[activeIndex].name } })}
62 | >
63 |
64 |
68 |
69 |
70 |
71 | {item.name}
72 |
73 |
74 |
75 | )
76 | })
77 |
78 | }
79 |
80 |
81 |
82 |
83 | >
84 |
85 | )
86 | }
87 | const styles = StyleSheet.create({
88 | container: {
89 | flex: 1,
90 | backgroundColor: 'white',
91 | flexDirection: 'row',
92 | justifyContent: 'space-between',
93 | paddingTop: 15,
94 | },
95 | categoriesWrapper: {
96 | width: 85,
97 | paddingLeft: 5,
98 | },
99 | image: {
100 | width: 50,
101 | height: 50,
102 | resizeMode: 'contain',
103 | marginBottom: 10,
104 | },
105 | categoryItem: {
106 | borderWidth: 1,
107 | borderColor: 'rgba(0,0,0,0.05)',
108 | alignItems: 'center',
109 | justifyContent: 'center',
110 | paddingBottom: 12,
111 | paddingTop: 10,
112 | borderRadius: 8,
113 | marginBottom: 10,
114 | elevation: 3,
115 | backgroundColor: 'white',
116 | },
117 | categoryItemActive: {
118 | borderWidth: 2,
119 | borderColor: COLORS.green
120 | },
121 | categoryText: {
122 | fontSize: 8,
123 | fontWeight: 'bold',
124 | color: '#70706f',
125 | paddingHorizontal: 5,
126 | lineHeight: 11,
127 | width: 68,
128 | textAlign: 'center',
129 | },
130 | categoryTextActive: {
131 | color: '#19a102',
132 | }
133 | ,
134 | subCategoryWrapper: {
135 | paddingHorizontal: 10,
136 | width: Dimensions.get('window').width - 100,
137 | position: 'relative',
138 | flexDirection: 'row',
139 | justifyContent: 'space-between',
140 | flexWrap: 'wrap',
141 | },
142 | subCategoryItem: {
143 | width: 80,
144 | height: 165,
145 | alignItems: 'center',
146 | },
147 | subCatImageWrapper: {
148 | marginBottom: 14,
149 | borderWidth: 1,
150 | padding: 6,
151 | borderColor: 'rgba(0,0,0,0.002)',
152 | elevation: 2,
153 | backgroundColor: 'white',
154 | borderRadius: 8,
155 | },
156 | subCatImage: {
157 | width: 75,
158 | height: 105,
159 | resizeMode: 'contain',
160 | },
161 | subCategoryText: {
162 | fontSize: 10,
163 | fontWeight: 'bold',
164 | color: '#70706f',
165 | lineHeight: 11,
166 | textAlign: 'center',
167 | }
168 |
169 | })
170 |
171 | export default CategoriesScreen
--------------------------------------------------------------------------------
/Client/src/view/components/general/ProductCard.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import { Dimensions, Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
3 | import COLORS from '../../../consts/colors';
4 | import { MaterialCommunityIcons } from 'react-native-vector-icons';
5 | import { AuthState } from '../../../Context/AuthContext';
6 | import { Rating } from 'react-native-ratings';
7 | import { FavoriteState } from '../../../Context/FavoriteContext';
8 | import CardImageSlider from './CardImageSlider';
9 | import { NGROK_URL } from '@env'
10 |
11 | const ProductCard = ({ product, navigation }) => {
12 | const { userInfo } = AuthState()
13 | const userId = userInfo.data.id
14 | const { addItemToFavoriteList, removeItemFromFavoriteList } = FavoriteState()
15 | const [likeStatus, setLikeStatus] = useState(false)
16 | const toggleLikeStatus = () => {
17 | setLikeStatus(!likeStatus)
18 | if (!likeStatus) {
19 | addItemToFavoriteList(product._id)
20 | }
21 | else {
22 | removeItemFromFavoriteList(product._id)
23 | }
24 | }
25 |
26 | useEffect(() => {
27 | setLikeStatus(product.likes.includes(userId))
28 |
29 | }, [product])
30 |
31 | return (
32 | <>
33 |
34 |
35 |
38 | {
41 | toggleLikeStatus()
42 | }}
43 | >
44 |
51 |
52 |
53 |
54 |
55 |
56 | {
58 | navigation.navigate('Details', { productId: product._id })
59 | }}
60 | >
61 | {product.images.length == 1 ?
62 |
67 | :
68 |
71 | }
72 |
73 |
74 |
75 | {
77 | navigation.navigate('Details', { productId: product._id })
78 | }}
79 |
80 | >
81 |
82 | {product?.seller} {product.name.length > 36 ? product.name.substring(0, 36) + '...' : product.name}
83 |
84 |
85 |
86 |
87 |
103 | ({product.comments.length})
104 |
105 |
106 | {product.price} TL
107 |
108 |
109 |
110 |
111 |
112 | >
113 |
114 | )
115 | }
116 | const styles = StyleSheet.create({
117 |
118 | card: {
119 | height: 322,
120 | backgroundColor: 'white',
121 | width: Dimensions.get('screen').width / 2 - 20,
122 | borderRadius: 10,
123 | marginBottom: 20,
124 | padding: 10,
125 | marginHorizontal: 2,
126 | elevation: 3,
127 | },
128 | cardImgContainer: {
129 | height: 185,
130 | overflow: 'hidden',
131 | marginBottom: 7,
132 | marginTop: 8,
133 | },
134 | cardImg: {
135 | height: 150,
136 | resizeMode: 'contain',
137 | width: '100%',
138 | height: '100%',
139 | },
140 | cardDetails: {
141 | marginLeft: 4,
142 | },
143 | cardTitle: {
144 | fontSize: 12,
145 | fontWeight: 'bold',
146 | color: COLORS.dark,
147 | marginTop: 2,
148 | color: 'gray',
149 | lineHeight: 17,
150 | // borderWidth :1,
151 | // borderColor: 'red',
152 | height: 39,
153 | },
154 | heartIcon: {
155 | position: 'absolute',
156 | top: -2,
157 | borderRadius: 50,
158 | padding: 5,
159 | right: -5,
160 | zIndex: 5,
161 | },
162 | price: {
163 | fontSize: 13,
164 | fontWeight: 'bold',
165 | color: COLORS.green,
166 | marginTop: 13,
167 | marginLeft: 2
168 | },
169 | flex: {
170 | display: 'flex', flexDirection: 'row', alignItems: 'center'
171 | }
172 | })
173 |
174 | export default ProductCard;
--------------------------------------------------------------------------------
/Server/Controllers/store.js:
--------------------------------------------------------------------------------
1 | const asyncErrorWrapper = require("express-async-handler");
2 | const Order = require("../Models/order");
3 | const CustomError = require("../Helpers/error/CustomError");
4 | const OrderItem = require("../Models/orderitem");
5 |
6 | const getAllBaskettems = asyncErrorWrapper(async (req, res, next) => {
7 |
8 | const order = await Order.findOne({ user: req.user.id, complete: false })
9 | const orderItems = await OrderItem.find({ order: order._id, orderStatus: false })
10 | .populate( "product", "name price images seller banner subCategory")
11 | .populate({
12 | path: "product",
13 | populate: {
14 | path: 'banner',
15 | }
16 | })
17 | .populate({
18 | path: "product",
19 | populate: {
20 | path: 'subCategory',
21 | select: 'name '
22 | }
23 | })
24 |
25 |
26 |
27 |
28 | const totalPrice = orderItems.reduce((acc, item) => {
29 | return acc + item.product.price * item.quantity
30 | }, 0)
31 |
32 | return res.status(200).json({
33 | success: true,
34 | totalPrice,
35 | orderItems
36 | })
37 |
38 | })
39 |
40 | const addToBasket = asyncErrorWrapper(async (req, res, next) => {
41 | const { productId ,selectedSize } = req.body;
42 |
43 | if (!productId) {
44 | return next(new CustomError("Please provide a product id", 400))
45 | }
46 |
47 | const order = await Order.findOne({ user: req.user.id, complete: false })
48 |
49 | const orderItem = await OrderItem.findOne({
50 | product: productId, orderStatus: false,
51 | order: order._id,
52 | selectedSize: selectedSize
53 | })
54 |
55 | if (orderItem) {
56 | orderItem.quantity += 1;
57 | await orderItem.save()
58 | }
59 | else {
60 | const newOrderItem = await OrderItem.create({
61 | product: productId,
62 | order,
63 | quantity: 1,
64 | orderStatus: false,
65 | selectedSize: selectedSize
66 | })
67 | await newOrderItem.save()
68 |
69 | return res.status(200).json({
70 | success: true,
71 | orderItem: newOrderItem
72 | })
73 |
74 | }
75 |
76 | return res.status(200).json({
77 | success: true,
78 | orderItem
79 | })
80 |
81 | })
82 |
83 | const deleteBasketItem = asyncErrorWrapper(async (req, res, next) => {
84 | const {itemId} = req.params;
85 | const order = await Order.findOne({ user: req.user.id, complete: false })
86 |
87 | const orderItem = await OrderItem.findOne({
88 | _id: itemId,
89 | order: order._id,
90 | orderStatus: false
91 | })
92 |
93 | if (!orderItem) {
94 | return next(new CustomError("Order item not found", 400))
95 | }
96 |
97 | await orderItem.remove()
98 |
99 | return res.status(200).json({
100 | success: true,
101 | message: "Item removed from card"
102 | })
103 |
104 | })
105 |
106 | const deleteAllBasketItems = asyncErrorWrapper(async (req, res, next) => {
107 |
108 | const order = await Order.findOne({ user: req.user.id, complete: false })
109 |
110 | await OrderItem.deleteMany({ order: order._id, orderStatus: false })
111 |
112 | return res.status(200).json({
113 | success: true,
114 | message: "All items removed from card"
115 | })
116 |
117 | })
118 |
119 |
120 |
121 | const increaseQuantity = asyncErrorWrapper(async (req, res, next) => {
122 | const { itemId } = req.body;
123 |
124 | if (!itemId) {
125 | return next(new CustomError("Please provide a item id", 400))
126 | }
127 | const order = await Order.findOne({ user: req.user.id, complete: false })
128 |
129 | const orderItem = await OrderItem.findOne({
130 | _id: itemId,
131 | order: order._id,
132 | orderStatus: false
133 | })
134 | if (!orderItem) {
135 | return next(new CustomError("Order item not found", 400))
136 | }
137 | orderItem.quantity += 1;
138 | await orderItem.save()
139 | return res.status(200).json({
140 | success: true,
141 | message: "Quantity increased",
142 | })
143 | })
144 |
145 |
146 | const decreaseQuantity = asyncErrorWrapper(async (req, res, next) => {
147 | const { itemId } = req.body;
148 |
149 | if (!itemId) {
150 | return next(new CustomError("Please provide a product id", 400))
151 | }
152 | const order = await Order.findOne({ user: req.user.id, complete: false })
153 |
154 | const orderItem = await OrderItem.findOne({
155 | _id: itemId,
156 | order: order._id,
157 | orderStatus: false
158 | })
159 | if (!orderItem) {
160 | return next(new CustomError("Order item not found", 400))
161 | }
162 | orderItem.quantity -= 1;
163 |
164 | if (orderItem.quantity === 0) {
165 | await orderItem.remove()
166 | return res.status(200).json({
167 | success: true,
168 | message: "Order Item removed "
169 | })
170 | }
171 | await orderItem.save()
172 |
173 | return res.status(200).json({
174 | success: true,
175 | message: "Quantity increased",
176 | })
177 | })
178 |
179 |
180 | const getAllDeliveryItems = asyncErrorWrapper(async (req, res, next) => {
181 |
182 | const order = await Order.findOne({ user: req.user.id, complete: false })
183 |
184 | const orderItems = await OrderItem.find({ order: order._id, orderStatus: false }).select("quantity product selectedSize ")
185 | .populate({
186 | path: "product",
187 | select: "name price images seller banner",
188 | populate: {
189 | path: "banner",
190 | }
191 | })
192 |
193 | const productGroupSeller = orderItems.reduce((acc, item) => {
194 | const seller = item.product.seller
195 | if (!acc[seller]) {
196 | acc[seller] = {
197 | seller: item.product.seller,
198 | products: []
199 | }
200 | }
201 | acc[seller].products.push(item)
202 | return acc
203 | }, {})
204 |
205 |
206 | const totalPrice = orderItems.reduce((acc, item) => {
207 | return acc + item.product.price * item.quantity
208 | }, 0)
209 |
210 | return res.status(200).json({
211 | success: true,
212 | totalPrice,
213 | productGroupSeller
214 | })
215 |
216 | })
217 |
218 | module.exports = {
219 | addToBasket,
220 | getAllBaskettems,
221 | deleteBasketItem,
222 | deleteAllBasketItems,
223 | increaseQuantity,
224 | decreaseQuantity,
225 | getAllDeliveryItems,
226 | }
--------------------------------------------------------------------------------
/Client/src/view/components/checkout/BankCardBottomSheet.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { View, Text, StyleSheet, TouchableOpacity, FlatList, Image } from 'react-native'
3 | import BottomSheet from 'react-native-gesture-bottom-sheet'
4 | import { Ionicons } from 'react-native-vector-icons'
5 | import COLORS from '../../../consts/colors'
6 |
7 | const BankCardBottomSheet = ({ bottomSheet, bankCards, setSelectedCard }) => {
8 | return (
9 |
12 |
13 |
14 |
15 |
16 |
17 |
20 | Kart Seçiniz
21 |
22 | { bottomSheet.current.close() }}
24 | >
25 |
30 |
31 |
32 |
33 |
34 | item._id}
37 | renderItem={({ item }) => (
38 | {
51 | setSelectedCard({
52 | cardNumber: {
53 | value: item.number,
54 | isValid: true
55 | },
56 | cardName: {
57 | value: item.name,
58 | isValid: true
59 | },
60 | cardExpiredYear: {
61 | value: item.expiredYear,
62 | isValid: true
63 | },
64 | cardExpiredMonth: {
65 | value: item.expiredMonth,
66 | isValid: true
67 | },
68 | cardCvv: {
69 | value: item.cvv,
70 | isValid: true
71 | },
72 | })
73 |
74 | bottomSheet.current.close()
75 | }
76 | }
77 | >
78 |
79 |
80 |
83 | {item.name}
84 |
85 |
88 | {
89 | String(item.number).replace(
90 | String(item.number).substring(6, 12),
91 | '****'
92 | )
93 | }
94 |
95 |
96 |
97 |
98 |
99 | {
100 | String(item.number).substring(0, 1) === '4' &&
101 |
105 | ||
106 | String(item.number).substring(0, 1) === '5' &&
107 |
111 | ||
112 |
116 |
117 | }
118 |
119 |
120 | )}
121 | />
122 |
123 |
124 |
125 |
126 |
127 |
128 | )
129 | }
130 |
131 | const styles = StyleSheet.create({
132 | bottomSheetContent: {
133 | backgroundColor: 'white',
134 | borderWidth: 1,
135 | borderColor: COLORS.grey,
136 | height: 395,
137 | },
138 | bottomSheetContentHeader: {
139 | backgroundColor: 'rgba(0,0,0,0.06)',
140 | paddingVertical: 10,
141 | paddingHorizontal: 20,
142 | flexDirection: 'row',
143 | justifyContent: 'space-between',
144 | alignItems: 'center',
145 | marginBottom: 10,
146 | }
147 | })
148 |
149 | export default BankCardBottomSheet
--------------------------------------------------------------------------------
/Client/src/view/screens/FavoriteListScreen.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { StyleSheet, Text, View, Image, ScrollView, TouchableOpacity, Dimensions } from 'react-native'
3 | import { SafeAreaView } from 'react-native-safe-area-context'
4 | import { AuthState } from '../../Context/AuthContext'
5 | import { Entypo } from 'react-native-vector-icons'
6 | import COLORS from '../../consts/colors'
7 | import * as Progress from 'react-native-progress';
8 | import { FavoriteState } from '../../Context/FavoriteContext'
9 | import FavoriteCard from '../components/favorite/FavoriteCard'
10 |
11 | const FavoriteListScreen = ({ navigation, route }) => {
12 |
13 | const { userInfo } = AuthState()
14 | const { favoriteList, loading } = FavoriteState();
15 |
16 | return (
17 |
18 |
25 | {
26 | favoriteList.length > 0 &&
27 |
28 |
35 |
36 |
37 |
40 |
46 |
47 | {userInfo.data.username}
48 |
49 |
50 |
51 |
52 | Favorilerim
53 |
54 | {favoriteList.length} ürün
55 |
56 |
57 |
58 |
59 | }
60 |
61 |
72 |
73 |
74 | {favoriteList.map((item, index) => {
75 |
76 | return (
77 |
78 |
82 |
83 |
84 | )
85 | }) }
86 |
87 |
88 | {
89 | favoriteList.length === 0 &&
90 |
93 |
100 |
101 |
102 | Favorilerim
103 |
104 |
105 | Favorilerinizde ürün bulunmamaktadır.
106 |
107 | navigation.navigate('Home')}
115 | >
116 |
119 | Alışverişe Devam Et
120 |
121 |
122 |
123 | }
124 |
125 |
126 |
127 |
128 | )
129 | }
130 |
131 | const styles = StyleSheet.create({
132 | bannerSection: {
133 | height: 190,
134 | justifyContent: 'center',
135 | alignItems: 'center',
136 | borderWidth: 2,
137 | borderColor: 'red',
138 | flexDirection: 'row',
139 | justifyContent: 'space-between',
140 | },
141 | banner_right: {
142 | backgroundColor: 'rgb(239, 50, 49)',
143 | height: 190,
144 | width: Dimensions.get('screen').width - 170,
145 | paddingLeft: 15,
146 | paddingTop: 20,
147 | },
148 | banner_title: {
149 | color: COLORS.white,
150 | fontSize: 29,
151 | fontWeight: 'bold',
152 | marginBottom: 28,
153 | marginTop: 10,
154 | },
155 | banner_right_top: {
156 | flexDirection: 'row',
157 | alignItems: 'center',
158 | marginBottom: 20,
159 | },
160 | banner_text: {
161 | color: COLORS.white,
162 | fontWeight: 'bold',
163 | fontSize: 13
164 | },
165 | itemsContainer: {
166 | flexDirection: 'column',
167 | alignItems: 'center',
168 | backgroundColor: COLORS.white,
169 | },
170 | btnWrapper: {
171 | backgroundColor: COLORS.green,
172 | color: 'white',
173 | paddingVertical: 12,
174 | borderRadius: 5,
175 | fontWeight: 'bold',
176 | marginTop: 10,
177 | width: '90%',
178 | textAlign: 'center',
179 | },
180 | emptyFavoritesWrapper: {
181 | height: Dimensions.get('screen').height - 125,
182 | backgroundColor: '#fff',
183 | justifyContent: 'center',
184 | alignItems: 'center',
185 | },
186 | textLight: {
187 | color: '#413F42',
188 | fontSize: 12,
189 | color: '#757575',
190 | marginBottom: 10,
191 | },
192 | text: {
193 | fontWeight: 'bold',
194 | fontSize: 15,
195 | marginBottom: 10,
196 | color: '#575656',
197 | }
198 |
199 | })
200 |
201 | export default FavoriteListScreen
--------------------------------------------------------------------------------
/Server/Controllers/order.js:
--------------------------------------------------------------------------------
1 | const asyncErrorWrapper = require("express-async-handler");
2 | const Order = require("../Models/order");
3 | const OrderItem = require("../Models/orderitem");
4 | const { v4: uuidv4 } = require('uuid');
5 | const BankCard = require("../Models/bankCard");
6 | const Address = require("../Models/address");
7 | const Evaluation = require("../Models/evaluation");
8 |
9 | const completeOrder = asyncErrorWrapper(async (req, res, next) => {
10 |
11 | const { selectedCard, selectedAddress } = req.body;
12 |
13 | const order = await Order.findOne({ user: req.user.id, complete: false })
14 |
15 | const orderItems = await OrderItem.find({ order: order._id, orderStatus: false })
16 |
17 | orderItems.forEach(async (item) => {
18 | item.orderStatus = true;
19 | item.date_added = Date.now();
20 | await item.save()
21 | })
22 |
23 | order.complete = true;
24 | order.dateOrdered = Date.now();
25 |
26 | if(selectedAddress._id) {
27 | order.orderAddress = selectedAddress;
28 | }
29 | else {
30 | const address = await Address.findOne({
31 | user: req.user.id,
32 | title: selectedAddress.title,
33 | detail: selectedAddress.detail,
34 | city: selectedAddress.city,
35 | })
36 | order.orderAddress = address._id;
37 | }
38 |
39 |
40 |
41 | const bankCard = await BankCard.findOne({ user: req.user.id, number: selectedCard.cardNumber.value })
42 |
43 | if (!bankCard) {
44 |
45 | const cardNumber = selectedCard.cardNumber.value.replace(/\s/g, '');
46 |
47 | const cardType = cardNumber.at(0) === "4" && "Visa" || cardNumber.at(0) === "5" && "MasterCard" || cardNumber.at(0) === "3" && "American Express" || "1.";
48 |
49 | const newBankCard = await BankCard.create({
50 | user: req.user.id,
51 | number: cardNumber,
52 | name: cardType + "Kartım",
53 | expiredMonth: selectedCard.cardExpiredMonth.value,
54 | expiredYear: selectedCard.cardExpiredYear.value,
55 | cvv: selectedCard.cardCvv.value
56 | })
57 | order.orderPayment = newBankCard._id;
58 |
59 | }
60 | else {
61 |
62 | order.orderPayment = bankCard._id;
63 | }
64 |
65 | await order.save()
66 |
67 |
68 | await Order.create({
69 | user: req.user.id,
70 | complete: false,
71 | transaction_id: uuidv4()
72 | })
73 |
74 | return res.status(200).json({
75 | success: true,
76 | message: "Order completed"
77 | })
78 |
79 |
80 | })
81 |
82 | const getAllMyOrders = asyncErrorWrapper(async (req, res, next) => {
83 |
84 | const orders = await Order.find({ user: req.user.id, complete: true }).select("dateOrdered user transaction_id ").sort({ dateOrdered: -1 })
85 |
86 | const orderContent = []
87 |
88 | await Promise.all(orders.map(async (order) => {
89 | const orderItems = await OrderItem.find({ order: order._id, orderStatus: true }).select("-order -orderStatus")
90 | .populate({
91 | path: "product",
92 | select: "name price images seller banner",
93 | populate: {
94 | path: "banner",
95 | }
96 | }).sort({ date_added: -1 })
97 |
98 | const totalPrice = orderItems.reduce((acc, item) => {
99 |
100 | return acc + item.product.price * item.quantity
101 | }, 0)
102 |
103 | orderContent.push({
104 | order,
105 | orderItems,
106 | totalPrice
107 | })
108 |
109 |
110 | }))
111 |
112 |
113 | orderContent.sort((a, b) => {
114 | return new Date(b.order.dateOrdered) - new Date(a.order.dateOrdered)
115 | })
116 |
117 |
118 | return res.status(200).json({
119 | success: true,
120 | orderContent
121 | })
122 |
123 | })
124 |
125 | const getOrderDetails = asyncErrorWrapper(async (req, res, next) => {
126 |
127 | const { id } = req.params;
128 |
129 | const order = await Order.findById(id).select("dateOrdered transaction_id orderAddress orderPayment").populate({
130 | path: "orderAddress",
131 | select: "name surname phone address city district neighborhood detail"
132 | }).populate({
133 | path: "orderPayment",
134 | select: "number"
135 | })
136 | const orderItems = await OrderItem.find({ order: order._id, orderStatus: true }).select("quantity product selectedSize ")
137 | .populate({
138 | path: "product",
139 | select: "name price images seller banner",
140 | populate: {
141 | path: "banner",
142 | }
143 | })
144 |
145 | const totalPrice = orderItems.reduce((acc, item) => {
146 |
147 | return acc + item.product.price * item.quantity
148 |
149 | }, 0)
150 |
151 | const productGroupSeller = orderItems.reduce((acc, item) => {
152 | const seller = item.product.seller
153 |
154 | if (!acc[seller]) {
155 | acc[seller] = {
156 | banner: item.product.banner,
157 | seller: item.product.seller,
158 | products: []
159 | }
160 | }
161 | acc[seller].products.push(item)
162 | return acc
163 | }, {})
164 |
165 |
166 | return res.status(200).json({
167 | success: true,
168 | order,
169 | totalPrice,
170 | orderItems: productGroupSeller,
171 | totalItemCount: orderItems.length
172 | })
173 |
174 | })
175 |
176 | const getAllEvaluatableItems = asyncErrorWrapper(async (req, res, next) => {
177 |
178 | const items = await Evaluation.find({ user: req.user.id, appravolStatus: false }).populate({
179 | path: "product",
180 | select: "name banner images price averageRating",
181 | populate: {
182 | path: "banner",
183 | select: "deliveryTime name"
184 | }
185 | }).
186 | populate({
187 | path: "orderItem",
188 | select: "date_added",
189 | }).sort({ date_added: -1 })
190 |
191 |
192 | return res.status(200).json({
193 | success: true,
194 | len: items.length,
195 | items
196 | })
197 |
198 | })
199 |
200 | const getAllApprovedEvaluatableItems = asyncErrorWrapper(async (req, res, next) => {
201 |
202 | const approvedItems = await Evaluation.find({
203 | user: req.user.id,
204 | appravolStatus: true,
205 | comment: { $ne: null }
206 | }).populate({
207 |
208 | path: "product",
209 | select: "name banner images price ",
210 | populate: {
211 | path: "banner",
212 | select: "name"
213 | }
214 | }).populate({
215 | path: "comment",
216 | select: "content rating nameVisible",
217 | })
218 |
219 | return res.status(200).json({
220 | success: true,
221 | approvedItems
222 | })
223 |
224 | })
225 |
226 |
227 | module.exports = {
228 | completeOrder,
229 | getAllMyOrders,
230 | getOrderDetails,
231 | getAllEvaluatableItems,
232 | getAllApprovedEvaluatableItems
233 | }
--------------------------------------------------------------------------------
/Client/src/view/screens/CheckoutScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import { Text, View, StyleSheet, TouchableOpacity, Image, ScrollView, Keyboard } from 'react-native'
3 | import { SafeAreaView } from 'react-native-safe-area-context'
4 | import { Ionicons } from 'react-native-vector-icons'
5 | import StoreService from '../../services/StoreService'
6 | import FixedConfirmationField from '../components/general/FixedConfirmationField'
7 | import PaymentSection from '../components/checkout/PaymentSection'
8 | import SelectAddressSection from '../components/checkout/SelectAddressSection'
9 | import AddressService from '../../services/AddressService'
10 | import BankCardService from '../../services/BankCardService'
11 | import DeliveryOptions from '../components/checkout/DeliveryOptions'
12 | import ConfirmationField from '../components/checkout/ConfirmationField'
13 | import ContractForms from '../components/checkout/ContractForms'
14 |
15 | const CheckoutScreen = ({ navigation, route }) => {
16 | const [confirmChecked, setConfirmChecked] = useState({
17 | value: false,
18 | error: null
19 | })
20 | const [deliveryItems, setDeliveryItems] = useState({})
21 | const [totalPrice, setTotalPrice] = useState(0)
22 | const [addresses, setAddresses] = useState([])
23 | const [bankCards, setBankCards] = useState([])
24 | const [selectedCard, setSelectedCard] = useState({
25 | cardNumber: {
26 | value: null,
27 | isValid: false
28 | },
29 | cardName: {
30 | value: null,
31 | isValid: false
32 | },
33 | cardExpiredYear: {
34 | value: null,
35 | isValid: false
36 | },
37 | cardExpiredMonth: {
38 | value: null,
39 | isValid: false
40 | },
41 | cardCvv: {
42 | value: null,
43 | isValid: false
44 | },
45 | })
46 | const [selectedAddress, setSelectedAddress] = useState({
47 | title: null,
48 | detail: null,
49 | city: null,
50 | district: null,
51 | neighborhood: null,
52 | name: null,
53 | surname: null,
54 | phone: null,
55 | isValid: false
56 | })
57 | const [keyboardVisible, setKeyboardVisible] = useState(false)
58 | const [disabled, setDisabled] = useState(false)
59 |
60 | const getAllDeliveryItems = async () => {
61 | const { data } = await StoreService.getAllDeliveryItems();
62 | setDeliveryItems(data.productGroupSeller)
63 | setTotalPrice(data.totalPrice)
64 | }
65 |
66 | const getAllAddresses = async (status = false) => {
67 | try {
68 | const { data } = await AddressService.getAllAddresses()
69 | setAddresses(data.addresses)
70 | if (!status) {
71 | setSelectedAddress(data.addresses[0])
72 | }
73 |
74 | } catch (error) {
75 | setAddresses([])
76 | setSelectedAddress({})
77 | }
78 | }
79 |
80 | const getAllBankCards = async () => {
81 | try {
82 | const { data } = await BankCardService.getAllBankCards()
83 | setBankCards(data.bankCards)
84 | }
85 | catch (error) {
86 | setBankCards([])
87 | }
88 | }
89 |
90 | useEffect(() => {
91 | getAllDeliveryItems()
92 | getAllAddresses()
93 | getAllBankCards()
94 |
95 | }, [])
96 |
97 | useEffect(() => {
98 | const unsubscribe = navigation.addListener('focus', () => {
99 | if (route?.params?.address) {
100 | getAllAddresses(true)
101 | setSelectedAddress(route?.params?.address)
102 | }
103 | } )
104 |
105 | return unsubscribe
106 |
107 | }, [route.params])
108 |
109 | useEffect(() => {
110 | Keyboard.addListener("keyboardDidShow", () => {
111 | setKeyboardVisible(true)
112 | })
113 | Keyboard.addListener("keyboardDidHide", () => {
114 | setKeyboardVisible(false)
115 | })
116 | }, [])
117 |
118 | return (
119 |
120 |
126 |
127 |
128 |
129 | navigation.goBack()}
134 | />
135 |
136 | Güvenli Ödeme
141 |
148 |
149 |
155 |
161 |
162 |
167 |
168 |
172 |
173 |
176 |
177 |
178 |
179 |
180 |
181 |
182 | {!keyboardVisible &&
195 | }
196 |
197 |
198 |
199 | )
200 | }
201 |
202 |
203 | const styles = StyleSheet.create({
204 | headerSection: {
205 | height: 60,
206 | padding: 18,
207 | backgroundColor: 'white',
208 | elevation: 5,
209 | flexDirection: 'row',
210 | justifyContent: 'space-between',
211 | alignItems: 'center',
212 | zIndex: 1,
213 | },
214 |
215 | })
216 |
217 |
218 | export default CheckoutScreen
--------------------------------------------------------------------------------