diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2deef83 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +node_modules +npm-debug.log +Dockerfile +docker-compose.yml +.dockerignore \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e0c5f52..3170cd8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,8 +17,9 @@ env: CLOUDINARY_API_SECRET: ${{secrets.CLOUDINARY_API_SECRET}} GOOGLE_CLIENT_ID: ${{secrets.GOOGLE_CLIENT_ID}} GOOGLE_CLIENT_SECRET: ${{secrets.GOOGLE_CLIENT_SECRET}} - + STRIPE_SECRET_KEY: ${{secrets.STRIPE_SECRET_KEYT}} + jobs: build-lint-test-coverage: runs-on: ubuntu-latest diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5190e01 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM node:20-alpine + +WORKDIR /app + +COPY package.json . + +RUN npm install + +COPY . . + +EXPOSE $PORT + +CMD ["npm", "run", "dev"] diff --git a/Intents/cancel.json b/Intents/cancel.json new file mode 100644 index 0000000..0171b1b --- /dev/null +++ b/Intents/cancel.json @@ -0,0 +1,14 @@ +{ + "intent": "cancel", + "utterances": [ + "Can I cancel my order?", + "Why is my order cancelled?", + "How do I can cancel my order", + "Can I cancel an order" + ], + "responses": [ + "Order can only be cancelled within 7 days of placement. Digital goods do not qualify for refund.", + "Visit my orders page to check status of specific orders." + ] + } + \ No newline at end of file diff --git a/Intents/confirm.json b/Intents/confirm.json new file mode 100644 index 0000000..5c708ec --- /dev/null +++ b/Intents/confirm.json @@ -0,0 +1,14 @@ +{ + "intent": "confirm", + "utterances": [ + "My payment was processed successfully but I didn't get any order confirmation. What should I do?", + "Payment was completed but no confirmation", + "Order was not confirmed", + "Money deducted but order is not confirmed" + ], + "responses": [ + "knight sends you an Email & SMS upon confirmation of your order. If it requires manual confirmation, our team will contact you within 24 hours after order placement. Delay in confirmation SMS may occur due to network error, you may receive it with a delay.", + "It takes upto 24 hours for confirmation, please bear with us! Type Need more help for more assistance. " + ] + } + \ No newline at end of file diff --git a/Intents/delay.json b/Intents/delay.json new file mode 100644 index 0000000..52d0cdf --- /dev/null +++ b/Intents/delay.json @@ -0,0 +1,16 @@ +{ + "intent": "delay", + "utterances": [ + "Why is my order delayed? ", + "Did my order get delayed?", + "Why is my order not delivered yet?", + "When do I get my delivery?" + ], + "responses": [ + "We are really sorry if there has been a delay in your order! If your order is within delivery period, We recommend you to please wait during this period since our Delivery Heroes make 3 attempts to deliver your order!", + "If your order is past up to 3 days after delivery period, There may be logistic issues causing a delay in delivery. Please type 'Common reasons for delivery delay' to know more about this.", + "We appreciate if you could wait for your items as most orders are delivered successfully within this period.", + "If your order is past more than 3 days, Since there may be unexpected issues causing delivery delays, you can click on 'Need more Help' for further assistance." + ] + } + \ No newline at end of file diff --git a/Intents/delivery.json b/Intents/delivery.json new file mode 100644 index 0000000..d3b969b --- /dev/null +++ b/Intents/delivery.json @@ -0,0 +1,15 @@ +{ + "intent": "delivery", + "utterances": [ + "How long does delivery take?", + "How long does shipping take?", + "Please Tell me about my delivery", + "When do I get my delivery?", + "Why is my order not delivered yet" + ], + "responses": [ + "Delivery takes 2-4 days. Please bear with us!", + "Shipping takes 2-4 days. Please bear with us!" + ] + } + \ No newline at end of file diff --git a/Intents/greetings.bye.json b/Intents/greetings.bye.json new file mode 100644 index 0000000..b9aa6f0 --- /dev/null +++ b/Intents/greetings.bye.json @@ -0,0 +1,18 @@ +{ + "intent": "goodbye", + "utterances": [ + "goodbye", + "bye take care", + "see you later", + "bye for now", + "i must go" + ], + "responses": [ + "see you soon!", + "Till next time", + "bye bye", + "have a great day", + "See you later, thanks for visiting. Hope I was able to help!", + "Have a nice day. Hope I was able to help!" + ] +} \ No newline at end of file diff --git a/Intents/greetings.hello.json b/Intents/greetings.hello.json new file mode 100644 index 0000000..7926f49 --- /dev/null +++ b/Intents/greetings.hello.json @@ -0,0 +1,19 @@ +{ + "intent": "greetings", + "utterances": [ + "hello", + "hi", + "howdy", + "Greetings", + "Is anyone there?", + "Hello", + "Good day" + ], + "responses": [ + "Hey :-) My name is knight!", + "Hello, thanks for visiting. My name is knight!", + "Hi there, My name is knight!. What can I do for you?", + "Hi there, My name is knight! How can I help?" + ] + } + \ No newline at end of file diff --git a/Intents/items.json b/Intents/items.json new file mode 100644 index 0000000..3a34861 --- /dev/null +++ b/Intents/items.json @@ -0,0 +1,23 @@ +{ + "intent": "items", + "utterances": [ + "Which items do you have?", + "What kinds of items are there?", + "What do you sell?", + "What do you offer?", + "What can I buy?", + "I'm looking for...", + "Do you have any...", + "I'm interested in...", + "Can I see what you have in...", + "I want to buy a...", + "I'm looking for something like this...", + "What are your most popular items?", + "What are some of your best deals?", + "Do you have any new arrivals?" + ], + "responses": [ + "Search your preference in our flagship store's search bar to see all available products. " + ] + } + \ No newline at end of file diff --git a/Intents/more.contact.json b/Intents/more.contact.json new file mode 100644 index 0000000..df773ac --- /dev/null +++ b/Intents/more.contact.json @@ -0,0 +1,12 @@ +{ + "intent": "more_contact", + "utterances": [ + "Need more help", + "Help me more" + ], + "responses": [ + "Absolutely! How can I help you today? Here are some options based on your inquiry:", + "Sure, let me know what you need help with. Here are a few things I can assist you with:" + ] + } + \ No newline at end of file diff --git a/Intents/more.help.json b/Intents/more.help.json new file mode 100644 index 0000000..3ce18e9 --- /dev/null +++ b/Intents/more.help.json @@ -0,0 +1,19 @@ +{ + "intent": "more_help", + "utterances": [ + "can I talk to an agent", + "can I call customer service", + "customer support number", + "how to contact customer service", + "customer service number", + "contact number for help", + "helpline number", + "How to become a seller", + "How to contact a seller" + ], + "responses": [ + "Contact us for further information here: Phone: +250 780 000 000. Timings are from 09:00 AM to 05:00 PM from Monday to Saturday.", + "For immediate assistance, you can contact our customer service team at +250 780 000 000. Our hours are from 09:00 AM to 05:00 PM, Monday to Saturday." + ] + } + \ No newline at end of file diff --git a/Intents/order.json b/Intents/order.json new file mode 100644 index 0000000..b110674 --- /dev/null +++ b/Intents/order.json @@ -0,0 +1,12 @@ +{ + "intent": "order_status", + "utterances": [ + "What is my order status", + "I want to know my return status", + "How to return status" + ], + "responses": [ + "Please visit the My Orders page for a list of your confirmed orders." + ] + } + \ No newline at end of file diff --git a/Intents/payments.json b/Intents/payments.json new file mode 100644 index 0000000..2243fa3 --- /dev/null +++ b/Intents/payments.json @@ -0,0 +1,15 @@ +{ + "intent": "payments", + "utterances": [ + "Do you take credit cards?", + "Do you accept Mastercard?", + "Can I pay with Cash?", + "Are you cash only?", + "What are your payment methods?", + "How do I pay?" + ], + "responses": [ + "We accept VISA and Mastercard" + ] + } + \ No newline at end of file diff --git a/Intents/personal.json b/Intents/personal.json new file mode 100644 index 0000000..fa32b95 --- /dev/null +++ b/Intents/personal.json @@ -0,0 +1,12 @@ +{ + "intent": "personal", + "utterances": [ + "How are you?", + "How are you doing?", + "How is your day?" + ], + "responses": [ + "I'm good, all's good, thanks. How about you?" + ] + } + \ No newline at end of file diff --git a/Intents/profile.info.json b/Intents/profile.info.json new file mode 100644 index 0000000..229b6ac --- /dev/null +++ b/Intents/profile.info.json @@ -0,0 +1,15 @@ +{ + "intent": "profile_info", + "utterances": [ + "How can I change my profile information", + "I want to change my password", + "I want to change my phone number", + "I want to change my address", + "I want to Reset my password", + "I want to delete my account", + "delete my account" + ], + "responses": [ + "You can easily add or change your account details by following the steps below: Step 1: Click on 'Account', Step 2: Click on 'Manage my account' from the icon, Step 3: You can change or edit your name, address, email address, mobile number, etc., Step 4: Fill in the required details, and click on Save. Note: You can also change your delivery address from the Checkout page before proceeding to pay." + ] + } \ No newline at end of file diff --git a/Intents/reason.json b/Intents/reason.json new file mode 100644 index 0000000..4c7b831 --- /dev/null +++ b/Intents/reason.json @@ -0,0 +1,13 @@ +{ + "intent": "reasons", + "utterances": [ + "Common reasons for delivery delay", + "common reasons for delivery delay", + "reasons for delay", + "delivery delay" + ], + "responses": [ + "Reasons include Seller Sourcing Issues, Courier Issues, Cross Border shipment delay, Wrong Address or Phone Number, and Unavailability of Customer. " + ] + } + \ No newline at end of file diff --git a/Intents/refund.demanding.json b/Intents/refund.demanding.json new file mode 100644 index 0000000..70e004e --- /dev/null +++ b/Intents/refund.demanding.json @@ -0,0 +1,14 @@ +{ + "intent": "demandin_refund", + "utterances": [ + "Can I refund an item.", + "I want to refund an item", + "can I refund my order", + "Are refunds available" + ], + "responses": [ + "Refund can only be issued within 7 days of placement. Digital goods do not qualify for refund.", + "Visit my orders page to check for specific orders." + ] + } + \ No newline at end of file diff --git a/Intents/refund.status.json b/Intents/refund.status.json new file mode 100644 index 0000000..a1f1361 --- /dev/null +++ b/Intents/refund.status.json @@ -0,0 +1,14 @@ +{ + "intent": "refund_status", + "utterances": [ + "Why is the status Refunded when it's not credited?", + "No refund even though status is refunded", + "No refund when status says refunded", + "I did not receive my refund money", + "Refund money not received" + ], + "responses": [ + "Please be patient as refunds take upto 30 days to receive into bank. " + ] + } + diff --git a/Intents/thanks.json b/Intents/thanks.json new file mode 100644 index 0000000..4d0fbb0 --- /dev/null +++ b/Intents/thanks.json @@ -0,0 +1,13 @@ +{ + "intent": "thanks", + "utterances": ["Thanks", + "Thank you", + "That's helpful", + "Thank's a lot!", + "thx", + "thnks"], + "responses": ["Happy to help!", + "Any time!", + "My pleasure"] + } + \ No newline at end of file diff --git a/Intents/track.json b/Intents/track.json new file mode 100644 index 0000000..a130950 --- /dev/null +++ b/Intents/track.json @@ -0,0 +1,13 @@ +{ + "intent": "track", + "utterances": [ + "How can I track my order", + "I want to track my order", + "Can I track my order", + "Track order" + ], + "responses": [ + "Visit the order page, click on the specific order, select 'track my order', and check the status" + ] + } + \ No newline at end of file diff --git a/Intents/user.response.json b/Intents/user.response.json new file mode 100644 index 0000000..998d545 --- /dev/null +++ b/Intents/user.response.json @@ -0,0 +1,14 @@ +{ + "intent": "user_response", + "utterances": [ + "I'm good", + "Im good", + "Im doing good", + "I am good", + "I am okay" + ], + "responses": [ + "Great to hear you are doing good." + ] + } + \ No newline at end of file diff --git a/Intents/voucher.json b/Intents/voucher.json new file mode 100644 index 0000000..cc75c1f --- /dev/null +++ b/Intents/voucher.json @@ -0,0 +1,12 @@ +{ + "intent": "use_voucher", + "utterances": [ + "How to use a voucher?", + "Can I use a voucher?", + "How to use a voucher?" + ], + "responses": [ + "You can add a voucher by clicking on My Cart > Check Out > Enter Voucher Code > APPLY. " + ] + } + \ No newline at end of file diff --git a/README.md b/README.md index 0d6c5f0..453c9d5 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,24 @@ logger.debug('This is a debug message'); npm test ``` +### Setting up docker and using it + +- Download and install docker + ``` + https://www.docker.com/products/docker-desktop/ + ``` +- Download Subsystem for Linux for none linux users +- Set environment varibles like database host to postgresdb + +- Building the image, you must navigate to the project directory in the terminal, then run + ``` + docker-compose up --build + ``` +- Stoping docker-compose container, run + ``` + docker-compose down + ``` + ## Authors - [Maxime Mizero](https://github.com/maxCastro1) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..99ed647 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,27 @@ +version: '3.8' + +services: + postgresdb: + image: postgres + environment: + POSTGRES_USER: $DEV_DB_USER + POSTGRES_PASSWORD: $DEV_DB_PASS + POSTGRES_DB: $DEV_DB_NAME + volumes: + - knights-data:/var/lib/postgresql/data + + node-app: + build: . + volumes: + - .:/app + - /app/node_modules + image: knights-app:1.0 + env_file: + - ./.env + ports: + - $PORT:$PORT + depends_on: + - postgresdb + +volumes: + knights-data: diff --git a/model.nlp b/model.nlp new file mode 100644 index 0000000..6aee508 --- /dev/null +++ b/model.nlp @@ -0,0 +1,5621 @@ +{ + "settings": { + "languages": [ + "en" + ], + "tag": "nlp", + "threshold": 0.5, + "autoLoad": true, + "autoSave": true, + "modelFileName": "model.nlp", + "executeActionsBeforeAnswers": false, + "calculateSentiment": true + }, + "nluManager": { + "settings": { + "tag": "nlu-manager" + }, + "locales": [ + "en" + ], + "languageNames": {}, + "domainManagers": { + "en": { + "settings": { + "locale": "en", + "trainByDomain": false, + "tag": "domain-manager-en", + "nluByDomain": { + "default": { + "className": "NeuralNlu", + "settings": {} + } + }, + "useStemDict": true + }, + "stemDict": { + "can,cancel,i,my,order": { + "intent": "cancel", + "domain": "default" + }, + "cancel,is,my,order,whi": { + "intent": "cancel", + "domain": "default" + }, + "can,cancel,do,how,i,my,order": { + "intent": "cancel", + "domain": "default" + }, + "an,can,cancel,i,order": { + "intent": "cancel", + "domain": "default" + }, + "ani,but,confirm,did,do,get,i,my,not,order,payment,process,should,success,was,what": { + "intent": "confirm", + "domain": "default" + }, + "but,complet,confirm,no,payment,was": { + "intent": "confirm", + "domain": "default" + }, + "confirm,not,order,was": { + "intent": "confirm", + "domain": "default" + }, + "but,confirm,deduct,is,money,not,order": { + "intent": "confirm", + "domain": "default" + }, + "delay,is,my,order,whi": { + "intent": "delay", + "domain": "default" + }, + "delay,did,get,my,order": { + "intent": "delay", + "domain": "default" + }, + "deliv,is,my,not,order,whi,yet": { + "intent": "delivery", + "domain": "default" + }, + "deliveri,do,get,i,my,when": { + "intent": "delivery", + "domain": "default" + }, + "deliveri,doe,how,long,take": { + "intent": "delivery", + "domain": "default" + }, + "doe,how,long,ship,take": { + "intent": "delivery", + "domain": "default" + }, + "about,deliveri,me,my,pleas,tell": { + "intent": "delivery", + "domain": "default" + }, + "goodby": { + "intent": "greetings.bye", + "domain": "default" + }, + "bye,care,take": { + "intent": "greetings.bye", + "domain": "default" + }, + "later,see,you": { + "intent": "greetings.bye", + "domain": "default" + }, + "bye,for,now": { + "intent": "greetings.bye", + "domain": "default" + }, + "go,i,must": { + "intent": "greetings.bye", + "domain": "default" + }, + "hello": { + "intent": "greetings.hello", + "domain": "default" + }, + "hi": { + "intent": "greetings.hello", + "domain": "default" + }, + "howdi": { + "intent": "greetings.hello", + "domain": "default" + }, + "greet": { + "intent": "greetings.hello", + "domain": "default" + }, + "anyon,is,there": { + "intent": "greetings.hello", + "domain": "default" + }, + "day,good": { + "intent": "greetings.hello", + "domain": "default" + }, + "do,have,item,which,you": { + "intent": "items", + "domain": "default" + }, + "are,item,kind,of,there,what": { + "intent": "items", + "domain": "default" + }, + "do,sell,what,you": { + "intent": "items", + "domain": "default" + }, + "do,offer,what,you": { + "intent": "items", + "domain": "default" + }, + "buy,can,i,what": { + "intent": "items", + "domain": "default" + }, + "am,for,i,look": { + "intent": "items", + "domain": "default" + }, + "ani,do,have,you": { + "intent": "items", + "domain": "default" + }, + "am,i,in,interest": { + "intent": "items", + "domain": "default" + }, + "can,have,i,in,see,what,you": { + "intent": "items", + "domain": "default" + }, + "a,buy,i,to,want": { + "intent": "items", + "domain": "default" + }, + "am,for,i,like,look,someth,this": { + "intent": "items", + "domain": "default" + }, + "are,item,most,popular,what,your": { + "intent": "items", + "domain": "default" + }, + "are,best,deal,of,some,what,your": { + "intent": "items", + "domain": "default" + }, + "ani,arriv,do,have,new,you": { + "intent": "items", + "domain": "default" + }, + "help,more,need": { + "intent": "more.contact", + "domain": "default" + }, + "help,me,more": { + "intent": "more.contact", + "domain": "default" + }, + "agent,an,can,i,talk,to": { + "intent": "more.help", + "domain": "default" + }, + "call,can,custom,i,servic": { + "intent": "more.help", + "domain": "default" + }, + "custom,number,support": { + "intent": "more.help", + "domain": "default" + }, + "contact,custom,how,servic,to": { + "intent": "more.help", + "domain": "default" + }, + "custom,number,servic": { + "intent": "more.help", + "domain": "default" + }, + "contact,for,help,number": { + "intent": "more.help", + "domain": "default" + }, + "helplin,number": { + "intent": "more.help", + "domain": "default" + }, + "a,becom,how,seller,to": { + "intent": "more.help", + "domain": "default" + }, + "a,contact,how,seller,to": { + "intent": "more.help", + "domain": "default" + }, + "is,my,order,status,what": { + "intent": "order", + "domain": "default" + }, + "i,know,my,return,status,to,want": { + "intent": "order", + "domain": "default" + }, + "how,return,status,to": { + "intent": "order", + "domain": "default" + }, + "card,credit,do,take,you": { + "intent": "payments", + "domain": "default" + }, + "accept,do,mastercard,you": { + "intent": "payments", + "domain": "default" + }, + "can,cash,i,pay,with": { + "intent": "payments", + "domain": "default" + }, + "are,cash,onli,you": { + "intent": "payments", + "domain": "default" + }, + "are,method,payment,what,your": { + "intent": "payments", + "domain": "default" + }, + "do,how,i,pay": { + "intent": "payments", + "domain": "default" + }, + "are,how,you": { + "intent": "personal", + "domain": "default" + }, + "are,do,how,you": { + "intent": "personal", + "domain": "default" + }, + "day,how,is,your": { + "intent": "personal", + "domain": "default" + }, + "can,chang,how,i,inform,my,profil": { + "intent": "profile.info", + "domain": "default" + }, + "chang,i,my,password,to,want": { + "intent": "profile.info", + "domain": "default" + }, + "chang,i,my,number,phone,to,want": { + "intent": "profile.info", + "domain": "default" + }, + "address,chang,i,my,to,want": { + "intent": "profile.info", + "domain": "default" + }, + "i,my,password,reset,to,want": { + "intent": "profile.info", + "domain": "default" + }, + "account,delet,i,my,to,want": { + "intent": "profile.info", + "domain": "default" + }, + "account,delet,my": { + "intent": "profile.info", + "domain": "default" + }, + "common,delay,deliveri,for,reason": { + "intent": "reason", + "domain": "default" + }, + "delay,for,reason": { + "intent": "reason", + "domain": "default" + }, + "delay,deliveri": { + "intent": "reason", + "domain": "default" + }, + "an,can,i,item,refund": { + "intent": "refund.demanding", + "domain": "default" + }, + "an,i,item,refund,to,want": { + "intent": "refund.demanding", + "domain": "default" + }, + "can,i,my,order,refund": { + "intent": "refund.demanding", + "domain": "default" + }, + "are,avail,refund": { + "intent": "refund.demanding", + "domain": "default" + }, + "credit,is,it,not,refund,status,the,when,whi": { + "intent": "refund.status", + "domain": "default" + }, + "even,is,no,refund,status,though": { + "intent": "refund.status", + "domain": "default" + }, + "no,refund,say,status,when": { + "intent": "refund.status", + "domain": "default" + }, + "did,i,money,my,not,receiv,refund": { + "intent": "refund.status", + "domain": "default" + }, + "money,not,receiv,refund": { + "intent": "refund.status", + "domain": "default" + }, + "thank": { + "intent": "thanks", + "domain": "default" + }, + "thank,you": { + "intent": "thanks", + "domain": "default" + }, + "help,is,that": { + "intent": "thanks", + "domain": "default" + }, + "a,is,lot,thank": { + "intent": "thanks", + "domain": "default" + }, + "thx": { + "intent": "thanks", + "domain": "default" + }, + "thnks": { + "intent": "thanks", + "domain": "default" + }, + "can,how,i,my,order,track": { + "intent": "track", + "domain": "default" + }, + "i,my,order,to,track,want": { + "intent": "track", + "domain": "default" + }, + "can,i,my,order,track": { + "intent": "track", + "domain": "default" + }, + "order,track": { + "intent": "track", + "domain": "default" + }, + "am,good,i": { + "intent": "user.response", + "domain": "default" + }, + "good,im": { + "intent": "user.response", + "domain": "default" + }, + "do,good,im": { + "intent": "user.response", + "domain": "default" + }, + "am,i,okay": { + "intent": "user.response", + "domain": "default" + }, + "a,how,to,use,voucher": { + "intent": "voucher", + "domain": "default" + }, + "a,can,i,use,voucher": { + "intent": "voucher", + "domain": "default" + } + }, + "intentDict": { + "cancel": "default", + "confirm": "default", + "delay": "default", + "delivery": "default", + "greetings.bye": "default", + "greetings.hello": "default", + "items": "default", + "more.contact": "default", + "more.help": "default", + "order": "default", + "payments": "default", + "personal": "default", + "profile.info": "default", + "reason": "default", + "refund.demanding": "default", + "refund.status": "default", + "thanks": "default", + "track": "default", + "user.response": "default", + "voucher": "default" + }, + "sentences": [ + { + "domain": "default", + "utterance": "Can I cancel my order?", + "intent": "cancel" + }, + { + "domain": "default", + "utterance": "Why is my order cancelled?", + "intent": "cancel" + }, + { + "domain": "default", + "utterance": "How do I can cancel my order", + "intent": "cancel" + }, + { + "domain": "default", + "utterance": "Can I cancel an order", + "intent": "cancel" + }, + { + "domain": "default", + "utterance": "My payment was processed successfully but I didn't get any order confirmation. What should I do?", + "intent": "confirm" + }, + { + "domain": "default", + "utterance": "Payment was completed but no confirmation", + "intent": "confirm" + }, + { + "domain": "default", + "utterance": "Order was not confirmed", + "intent": "confirm" + }, + { + "domain": "default", + "utterance": "Money deducted but order is not confirmed", + "intent": "confirm" + }, + { + "domain": "default", + "utterance": "Why is my order delayed? ", + "intent": "delay" + }, + { + "domain": "default", + "utterance": "Did my order get delayed?", + "intent": "delay" + }, + { + "domain": "default", + "utterance": "Why is my order not delivered yet?", + "intent": "delay" + }, + { + "domain": "default", + "utterance": "When do I get my delivery?", + "intent": "delay" + }, + { + "domain": "default", + "utterance": "How long does delivery take?", + "intent": "delivery" + }, + { + "domain": "default", + "utterance": "How long does shipping take?", + "intent": "delivery" + }, + { + "domain": "default", + "utterance": "Please Tell me about my delivery", + "intent": "delivery" + }, + { + "domain": "default", + "utterance": "When do I get my delivery?", + "intent": "delivery" + }, + { + "domain": "default", + "utterance": "Why is my order not delivered yet", + "intent": "delivery" + }, + { + "domain": "default", + "utterance": "goodbye", + "intent": "greetings.bye" + }, + { + "domain": "default", + "utterance": "bye take care", + "intent": "greetings.bye" + }, + { + "domain": "default", + "utterance": "see you later", + "intent": "greetings.bye" + }, + { + "domain": "default", + "utterance": "bye for now", + "intent": "greetings.bye" + }, + { + "domain": "default", + "utterance": "i must go", + "intent": "greetings.bye" + }, + { + "domain": "default", + "utterance": "hello", + "intent": "greetings.hello" + }, + { + "domain": "default", + "utterance": "hi", + "intent": "greetings.hello" + }, + { + "domain": "default", + "utterance": "howdy", + "intent": "greetings.hello" + }, + { + "domain": "default", + "utterance": "Greetings", + "intent": "greetings.hello" + }, + { + "domain": "default", + "utterance": "Is anyone there?", + "intent": "greetings.hello" + }, + { + "domain": "default", + "utterance": "Hello", + "intent": "greetings.hello" + }, + { + "domain": "default", + "utterance": "Good day", + "intent": "greetings.hello" + }, + { + "domain": "default", + "utterance": "Which items do you have?", + "intent": "items" + }, + { + "domain": "default", + "utterance": "What kinds of items are there?", + "intent": "items" + }, + { + "domain": "default", + "utterance": "What do you sell?", + "intent": "items" + }, + { + "domain": "default", + "utterance": "What do you offer?", + "intent": "items" + }, + { + "domain": "default", + "utterance": "What can I buy?", + "intent": "items" + }, + { + "domain": "default", + "utterance": "I'm looking for...", + "intent": "items" + }, + { + "domain": "default", + "utterance": "Do you have any...", + "intent": "items" + }, + { + "domain": "default", + "utterance": "I'm interested in...", + "intent": "items" + }, + { + "domain": "default", + "utterance": "Can I see what you have in...", + "intent": "items" + }, + { + "domain": "default", + "utterance": "I want to buy a...", + "intent": "items" + }, + { + "domain": "default", + "utterance": "I'm looking for something like this...", + "intent": "items" + }, + { + "domain": "default", + "utterance": "What are your most popular items?", + "intent": "items" + }, + { + "domain": "default", + "utterance": "What are some of your best deals?", + "intent": "items" + }, + { + "domain": "default", + "utterance": "Do you have any new arrivals?", + "intent": "items" + }, + { + "domain": "default", + "utterance": "Need more help", + "intent": "more.contact" + }, + { + "domain": "default", + "utterance": "Help me more", + "intent": "more.contact" + }, + { + "domain": "default", + "utterance": "can I talk to an agent", + "intent": "more.help" + }, + { + "domain": "default", + "utterance": "can I call customer service", + "intent": "more.help" + }, + { + "domain": "default", + "utterance": "customer support number", + "intent": "more.help" + }, + { + "domain": "default", + "utterance": "how to contact customer service", + "intent": "more.help" + }, + { + "domain": "default", + "utterance": "customer service number", + "intent": "more.help" + }, + { + "domain": "default", + "utterance": "contact number for help", + "intent": "more.help" + }, + { + "domain": "default", + "utterance": "helpline number", + "intent": "more.help" + }, + { + "domain": "default", + "utterance": "How to become a seller", + "intent": "more.help" + }, + { + "domain": "default", + "utterance": "How to contact a seller", + "intent": "more.help" + }, + { + "domain": "default", + "utterance": "What is my order status", + "intent": "order" + }, + { + "domain": "default", + "utterance": "I want to know my return status", + "intent": "order" + }, + { + "domain": "default", + "utterance": "How to return status", + "intent": "order" + }, + { + "domain": "default", + "utterance": "Do you take credit cards?", + "intent": "payments" + }, + { + "domain": "default", + "utterance": "Do you accept Mastercard?", + "intent": "payments" + }, + { + "domain": "default", + "utterance": "Can I pay with Cash?", + "intent": "payments" + }, + { + "domain": "default", + "utterance": "Are you cash only?", + "intent": "payments" + }, + { + "domain": "default", + "utterance": "What are your payment methods?", + "intent": "payments" + }, + { + "domain": "default", + "utterance": "How do I pay?", + "intent": "payments" + }, + { + "domain": "default", + "utterance": "How are you?", + "intent": "personal" + }, + { + "domain": "default", + "utterance": "How are you doing?", + "intent": "personal" + }, + { + "domain": "default", + "utterance": "How is your day?", + "intent": "personal" + }, + { + "domain": "default", + "utterance": "How can I change my profile information", + "intent": "profile.info" + }, + { + "domain": "default", + "utterance": "I want to change my password", + "intent": "profile.info" + }, + { + "domain": "default", + "utterance": "I want to change my phone number", + "intent": "profile.info" + }, + { + "domain": "default", + "utterance": "I want to change my address", + "intent": "profile.info" + }, + { + "domain": "default", + "utterance": "I want to Reset my password", + "intent": "profile.info" + }, + { + "domain": "default", + "utterance": "I want to delete my account", + "intent": "profile.info" + }, + { + "domain": "default", + "utterance": "delete my account", + "intent": "profile.info" + }, + { + "domain": "default", + "utterance": "Common reasons for delivery delay", + "intent": "reason" + }, + { + "domain": "default", + "utterance": "common reasons for delivery delay", + "intent": "reason" + }, + { + "domain": "default", + "utterance": "reasons for delay", + "intent": "reason" + }, + { + "domain": "default", + "utterance": "delivery delay", + "intent": "reason" + }, + { + "domain": "default", + "utterance": "Can I refund an item.", + "intent": "refund.demanding" + }, + { + "domain": "default", + "utterance": "I want to refund an item", + "intent": "refund.demanding" + }, + { + "domain": "default", + "utterance": "can I refund my order", + "intent": "refund.demanding" + }, + { + "domain": "default", + "utterance": "Are refunds available", + "intent": "refund.demanding" + }, + { + "domain": "default", + "utterance": "Why is the status Refunded when it's not credited?", + "intent": "refund.status" + }, + { + "domain": "default", + "utterance": "No refund even though status is refunded", + "intent": "refund.status" + }, + { + "domain": "default", + "utterance": "No refund when status says refunded", + "intent": "refund.status" + }, + { + "domain": "default", + "utterance": "I did not receive my refund money", + "intent": "refund.status" + }, + { + "domain": "default", + "utterance": "Refund money not received", + "intent": "refund.status" + }, + { + "domain": "default", + "utterance": "Thanks", + "intent": "thanks" + }, + { + "domain": "default", + "utterance": "Thank you", + "intent": "thanks" + }, + { + "domain": "default", + "utterance": "That's helpful", + "intent": "thanks" + }, + { + "domain": "default", + "utterance": "Thank's a lot!", + "intent": "thanks" + }, + { + "domain": "default", + "utterance": "thx", + "intent": "thanks" + }, + { + "domain": "default", + "utterance": "thnks", + "intent": "thanks" + }, + { + "domain": "default", + "utterance": "How can I track my order", + "intent": "track" + }, + { + "domain": "default", + "utterance": "I want to track my order", + "intent": "track" + }, + { + "domain": "default", + "utterance": "Can I track my order", + "intent": "track" + }, + { + "domain": "default", + "utterance": "Track order", + "intent": "track" + }, + { + "domain": "default", + "utterance": "I'm good", + "intent": "user.response" + }, + { + "domain": "default", + "utterance": "Im good", + "intent": "user.response" + }, + { + "domain": "default", + "utterance": "Im doing good", + "intent": "user.response" + }, + { + "domain": "default", + "utterance": "I am good", + "intent": "user.response" + }, + { + "domain": "default", + "utterance": "I am okay", + "intent": "user.response" + }, + { + "domain": "default", + "utterance": "How to use a voucher?", + "intent": "voucher" + }, + { + "domain": "default", + "utterance": "Can I use a voucher?", + "intent": "voucher" + }, + { + "domain": "default", + "utterance": "How to use a voucher?", + "intent": "voucher" + } + ], + "domains": { + "master_domain": { + "settings": { + "locale": "en", + "tag": "nlu-en", + "keepStopwords": true, + "nonefeatureValue": 1, + "nonedeltaMultiplier": 1.2, + "spellCheck": false, + "spellCheckDistance": 1, + "filterZeros": true, + "log": true + }, + "features": { + "can": 1, + "i": 1, + "cancel": 1, + "my": 1, + "order": 1, + "whi": 1, + "is": 1, + "how": 1, + "do": 1, + "an": 1, + "payment": 1, + "was": 1, + "process": 1, + "success": 1, + "but": 1, + "did": 1, + "not": 1, + "get": 1, + "ani": 1, + "confirm": 1, + "what": 1, + "should": 1, + "complet": 1, + "no": 1, + "money": 1, + "deduct": 1, + "delay": 1, + "deliv": 1, + "yet": 1, + "when": 1, + "deliveri": 1, + "long": 1, + "doe": 1, + "take": 1, + "ship": 1, + "pleas": 1, + "tell": 1, + "me": 1, + "about": 1, + "goodby": 1, + "bye": 1, + "care": 1, + "see": 1, + "you": 1, + "later": 1, + "for": 1, + "now": 1, + "must": 1, + "go": 1, + "hello": 1, + "hi": 1, + "howdi": 1, + "greet": 1, + "anyon": 1, + "there": 1, + "good": 1, + "day": 1, + "which": 1, + "item": 1, + "have": 1, + "kind": 1, + "of": 1, + "are": 1, + "sell": 1, + "offer": 1, + "buy": 1, + "am": 1, + "look": 1, + "interest": 1, + "in": 1, + "want": 1, + "to": 1, + "a": 1, + "someth": 1, + "like": 1, + "this": 1, + "your": 1, + "most": 1, + "popular": 1, + "some": 1, + "best": 1, + "deal": 1, + "new": 1, + "arriv": 1, + "need": 1, + "more": 1, + "help": 1, + "talk": 1, + "agent": 1, + "call": 1, + "custom": 1, + "servic": 1, + "support": 1, + "number": 1, + "contact": 1, + "helplin": 1, + "becom": 1, + "seller": 1, + "status": 1, + "know": 1, + "return": 1, + "credit": 1, + "card": 1, + "accept": 1, + "mastercard": 1, + "pay": 1, + "with": 1, + "cash": 1, + "onli": 1, + "method": 1, + "chang": 1, + "profil": 1, + "inform": 1, + "password": 1, + "phone": 1, + "address": 1, + "reset": 1, + "delet": 1, + "account": 1, + "common": 1, + "reason": 1, + "refund": 1, + "avail": 1, + "the": 1, + "it": 1, + "even": 1, + "though": 1, + "say": 1, + "receiv": 1, + "thank": 1, + "that": 1, + "lot": 1, + "thx": 1, + "thnks": 1, + "track": 1, + "im": 1, + "okay": 1, + "use": 1, + "voucher": 1 + }, + "intents": { + "cancel": 1, + "confirm": 1, + "delay": 1, + "delivery": 1, + "greetings.bye": 1, + "greetings.hello": 1, + "items": 1, + "more.contact": 1, + "more.help": 1, + "order": 1, + "payments": 1, + "personal": 1, + "profile.info": 1, + "reason": 1, + "refund.demanding": 1, + "refund.status": 1, + "thanks": 1, + "track": 1, + "user.response": 1, + "voucher": 1 + }, + "intentFeatures": { + "cancel": { + "can": 1, + "i": 1, + "cancel": 1, + "my": 1, + "order": 1, + "whi": 1, + "is": 1, + "how": 1, + "do": 1, + "an": 1 + }, + "confirm": { + "my": 1, + "payment": 1, + "was": 1, + "process": 1, + "success": 1, + "but": 1, + "i": 1, + "did": 1, + "not": 1, + "get": 1, + "ani": 1, + "order": 1, + "confirm": 1, + "what": 1, + "should": 1, + "do": 1, + "complet": 1, + "no": 1, + "money": 1, + "deduct": 1, + "is": 1 + }, + "delay": { + "whi": 1, + "is": 1, + "my": 1, + "order": 1, + "delay": 1, + "did": 1, + "get": 1, + "not": 1, + "deliv": 1, + "yet": 1, + "when": 1, + "do": 1, + "i": 1, + "deliveri": 1 + }, + "delivery": { + "how": 1, + "long": 1, + "doe": 1, + "deliveri": 1, + "take": 1, + "ship": 1, + "pleas": 1, + "tell": 1, + "me": 1, + "about": 1, + "my": 1, + "when": 1, + "do": 1, + "i": 1, + "get": 1, + "whi": 1, + "is": 1, + "order": 1, + "not": 1, + "deliv": 1, + "yet": 1 + }, + "greetings.bye": { + "goodby": 1, + "bye": 1, + "take": 1, + "care": 1, + "see": 1, + "you": 1, + "later": 1, + "for": 1, + "now": 1, + "i": 1, + "must": 1, + "go": 1 + }, + "greetings.hello": { + "hello": 1, + "hi": 1, + "howdi": 1, + "greet": 1, + "is": 1, + "anyon": 1, + "there": 1, + "good": 1, + "day": 1 + }, + "items": { + "which": 1, + "item": 1, + "do": 1, + "you": 1, + "have": 1, + "what": 1, + "kind": 1, + "of": 1, + "are": 1, + "there": 1, + "sell": 1, + "offer": 1, + "can": 1, + "i": 1, + "buy": 1, + "am": 1, + "look": 1, + "for": 1, + "ani": 1, + "interest": 1, + "in": 1, + "see": 1, + "want": 1, + "to": 1, + "a": 1, + "someth": 1, + "like": 1, + "this": 1, + "your": 1, + "most": 1, + "popular": 1, + "some": 1, + "best": 1, + "deal": 1, + "new": 1, + "arriv": 1 + }, + "more.contact": { + "need": 1, + "more": 1, + "help": 1, + "me": 1 + }, + "more.help": { + "can": 1, + "i": 1, + "talk": 1, + "to": 1, + "an": 1, + "agent": 1, + "call": 1, + "custom": 1, + "servic": 1, + "support": 1, + "number": 1, + "how": 1, + "contact": 1, + "for": 1, + "help": 1, + "helplin": 1, + "becom": 1, + "a": 1, + "seller": 1 + }, + "order": { + "what": 1, + "is": 1, + "my": 1, + "order": 1, + "status": 1, + "i": 1, + "want": 1, + "to": 1, + "know": 1, + "return": 1, + "how": 1 + }, + "payments": { + "do": 1, + "you": 1, + "take": 1, + "credit": 1, + "card": 1, + "accept": 1, + "mastercard": 1, + "can": 1, + "i": 1, + "pay": 1, + "with": 1, + "cash": 1, + "are": 1, + "onli": 1, + "what": 1, + "your": 1, + "payment": 1, + "method": 1, + "how": 1 + }, + "personal": { + "how": 1, + "are": 1, + "you": 1, + "do": 1, + "is": 1, + "your": 1, + "day": 1 + }, + "profile.info": { + "how": 1, + "can": 1, + "i": 1, + "chang": 1, + "my": 1, + "profil": 1, + "inform": 1, + "want": 1, + "to": 1, + "password": 1, + "phone": 1, + "number": 1, + "address": 1, + "reset": 1, + "delet": 1, + "account": 1 + }, + "reason": { + "common": 1, + "reason": 1, + "for": 1, + "deliveri": 1, + "delay": 1 + }, + "refund.demanding": { + "can": 1, + "i": 1, + "refund": 1, + "an": 1, + "item": 1, + "want": 1, + "to": 1, + "my": 1, + "order": 1, + "are": 1, + "avail": 1 + }, + "refund.status": { + "whi": 1, + "is": 1, + "the": 1, + "status": 1, + "refund": 1, + "when": 1, + "it": 1, + "not": 1, + "credit": 1, + "no": 1, + "even": 1, + "though": 1, + "say": 1, + "i": 1, + "did": 1, + "receiv": 1, + "my": 1, + "money": 1 + }, + "thanks": { + "thank": 1, + "you": 1, + "that": 1, + "is": 1, + "help": 1, + "a": 1, + "lot": 1, + "thx": 1, + "thnks": 1 + }, + "track": { + "how": 1, + "can": 1, + "i": 1, + "track": 1, + "my": 1, + "order": 1, + "want": 1, + "to": 1 + }, + "user.response": { + "i": 1, + "am": 1, + "good": 1, + "im": 1, + "do": 1, + "okay": 1 + }, + "voucher": { + "how": 1, + "to": 1, + "use": 1, + "a": 1, + "voucher": 1, + "can": 1, + "i": 1 + } + }, + "featuresToIntent": { + "can": [ + "cancel", + "items", + "more.help", + "payments", + "profile.info", + "refund.demanding", + "track", + "voucher" + ], + "i": [ + "cancel", + "confirm", + "delay", + "delivery", + "greetings.bye", + "items", + "more.help", + "order", + "payments", + "profile.info", + "refund.demanding", + "refund.status", + "track", + "user.response", + "voucher" + ], + "cancel": [ + "cancel" + ], + "my": [ + "cancel", + "confirm", + "delay", + "delivery", + "order", + "profile.info", + "refund.demanding", + "refund.status", + "track" + ], + "order": [ + "cancel", + "confirm", + "delay", + "delivery", + "order", + "refund.demanding", + "track" + ], + "whi": [ + "cancel", + "delay", + "delivery", + "refund.status" + ], + "is": [ + "cancel", + "confirm", + "delay", + "delivery", + "greetings.hello", + "order", + "personal", + "refund.status", + "thanks" + ], + "how": [ + "cancel", + "delivery", + "more.help", + "order", + "payments", + "personal", + "profile.info", + "track", + "voucher" + ], + "do": [ + "cancel", + "confirm", + "delay", + "delivery", + "items", + "payments", + "personal", + "user.response" + ], + "an": [ + "cancel", + "more.help", + "refund.demanding" + ], + "payment": [ + "confirm", + "payments" + ], + "was": [ + "confirm" + ], + "process": [ + "confirm" + ], + "success": [ + "confirm" + ], + "but": [ + "confirm" + ], + "did": [ + "confirm", + "delay", + "refund.status" + ], + "not": [ + "confirm", + "delay", + "delivery", + "refund.status" + ], + "get": [ + "confirm", + "delay", + "delivery" + ], + "ani": [ + "confirm", + "items" + ], + "confirm": [ + "confirm" + ], + "what": [ + "confirm", + "items", + "order", + "payments" + ], + "should": [ + "confirm" + ], + "complet": [ + "confirm" + ], + "no": [ + "confirm", + "refund.status" + ], + "money": [ + "confirm", + "refund.status" + ], + "deduct": [ + "confirm" + ], + "delay": [ + "delay", + "reason" + ], + "deliv": [ + "delay", + "delivery" + ], + "yet": [ + "delay", + "delivery" + ], + "when": [ + "delay", + "delivery", + "refund.status" + ], + "deliveri": [ + "delay", + "delivery", + "reason" + ], + "long": [ + "delivery" + ], + "doe": [ + "delivery" + ], + "take": [ + "delivery", + "greetings.bye", + "payments" + ], + "ship": [ + "delivery" + ], + "pleas": [ + "delivery" + ], + "tell": [ + "delivery" + ], + "me": [ + "delivery", + "more.contact" + ], + "about": [ + "delivery" + ], + "goodby": [ + "greetings.bye" + ], + "bye": [ + "greetings.bye" + ], + "care": [ + "greetings.bye" + ], + "see": [ + "greetings.bye", + "items" + ], + "you": [ + "greetings.bye", + "items", + "payments", + "personal", + "thanks" + ], + "later": [ + "greetings.bye" + ], + "for": [ + "greetings.bye", + "items", + "more.help", + "reason" + ], + "now": [ + "greetings.bye" + ], + "must": [ + "greetings.bye" + ], + "go": [ + "greetings.bye" + ], + "hello": [ + "greetings.hello" + ], + "hi": [ + "greetings.hello" + ], + "howdi": [ + "greetings.hello" + ], + "greet": [ + "greetings.hello" + ], + "anyon": [ + "greetings.hello" + ], + "there": [ + "greetings.hello", + "items" + ], + "good": [ + "greetings.hello", + "user.response" + ], + "day": [ + "greetings.hello", + "personal" + ], + "which": [ + "items" + ], + "item": [ + "items", + "refund.demanding" + ], + "have": [ + "items" + ], + "kind": [ + "items" + ], + "of": [ + "items" + ], + "are": [ + "items", + "payments", + "personal", + "refund.demanding" + ], + "sell": [ + "items" + ], + "offer": [ + "items" + ], + "buy": [ + "items" + ], + "am": [ + "items", + "user.response" + ], + "look": [ + "items" + ], + "interest": [ + "items" + ], + "in": [ + "items" + ], + "want": [ + "items", + "order", + "profile.info", + "refund.demanding", + "track" + ], + "to": [ + "items", + "more.help", + "order", + "profile.info", + "refund.demanding", + "track", + "voucher" + ], + "a": [ + "items", + "more.help", + "thanks", + "voucher" + ], + "someth": [ + "items" + ], + "like": [ + "items" + ], + "this": [ + "items" + ], + "your": [ + "items", + "payments", + "personal" + ], + "most": [ + "items" + ], + "popular": [ + "items" + ], + "some": [ + "items" + ], + "best": [ + "items" + ], + "deal": [ + "items" + ], + "new": [ + "items" + ], + "arriv": [ + "items" + ], + "need": [ + "more.contact" + ], + "more": [ + "more.contact" + ], + "help": [ + "more.contact", + "more.help", + "thanks" + ], + "talk": [ + "more.help" + ], + "agent": [ + "more.help" + ], + "call": [ + "more.help" + ], + "custom": [ + "more.help" + ], + "servic": [ + "more.help" + ], + "support": [ + "more.help" + ], + "number": [ + "more.help", + "profile.info" + ], + "contact": [ + "more.help" + ], + "helplin": [ + "more.help" + ], + "becom": [ + "more.help" + ], + "seller": [ + "more.help" + ], + "status": [ + "order", + "refund.status" + ], + "know": [ + "order" + ], + "return": [ + "order" + ], + "credit": [ + "payments", + "refund.status" + ], + "card": [ + "payments" + ], + "accept": [ + "payments" + ], + "mastercard": [ + "payments" + ], + "pay": [ + "payments" + ], + "with": [ + "payments" + ], + "cash": [ + "payments" + ], + "onli": [ + "payments" + ], + "method": [ + "payments" + ], + "chang": [ + "profile.info" + ], + "profil": [ + "profile.info" + ], + "inform": [ + "profile.info" + ], + "password": [ + "profile.info" + ], + "phone": [ + "profile.info" + ], + "address": [ + "profile.info" + ], + "reset": [ + "profile.info" + ], + "delet": [ + "profile.info" + ], + "account": [ + "profile.info" + ], + "common": [ + "reason" + ], + "reason": [ + "reason" + ], + "refund": [ + "refund.demanding", + "refund.status" + ], + "avail": [ + "refund.demanding" + ], + "the": [ + "refund.status" + ], + "it": [ + "refund.status" + ], + "even": [ + "refund.status" + ], + "though": [ + "refund.status" + ], + "say": [ + "refund.status" + ], + "receiv": [ + "refund.status" + ], + "thank": [ + "thanks" + ], + "that": [ + "thanks" + ], + "lot": [ + "thanks" + ], + "thx": [ + "thanks" + ], + "thnks": [ + "thanks" + ], + "track": [ + "track" + ], + "im": [ + "user.response" + ], + "okay": [ + "user.response" + ], + "use": [ + "voucher" + ], + "voucher": [ + "voucher" + ] + }, + "neuralNetwork": { + "settings": { + "locale": "en", + "tag": "nlu-en", + "keepStopwords": true, + "nonefeatureValue": 1, + "nonedeltaMultiplier": 1.2, + "spellCheck": false, + "spellCheckDistance": 1, + "filterZeros": true, + "log": true + }, + "features": [ + "can", + "i", + "cancel", + "my", + "order", + "whi", + "is", + "how", + "do", + "an", + "payment", + "was", + "process", + "success", + "but", + "did", + "not", + "get", + "ani", + "confirm", + "what", + "should", + "complet", + "no", + "money", + "deduct", + "delay", + "deliv", + "yet", + "when", + "deliveri", + "long", + "doe", + "take", + "ship", + "pleas", + "tell", + "me", + "about", + "goodby", + "bye", + "care", + "see", + "you", + "later", + "for", + "now", + "must", + "go", + "hello", + "hi", + "howdi", + "greet", + "anyon", + "there", + "good", + "day", + "which", + "item", + "have", + "kind", + "of", + "are", + "sell", + "offer", + "buy", + "am", + "look", + "interest", + "in", + "want", + "to", + "a", + "someth", + "like", + "this", + "your", + "most", + "popular", + "some", + "best", + "deal", + "new", + "arriv", + "need", + "more", + "help", + "talk", + "agent", + "call", + "custom", + "servic", + "support", + "number", + "contact", + "helplin", + "becom", + "seller", + "status", + "know", + "return", + "credit", + "card", + "accept", + "mastercard", + "pay", + "with", + "cash", + "onli", + "method", + "chang", + "profil", + "inform", + "password", + "phone", + "address", + "reset", + "delet", + "account", + "common", + "reason", + "refund", + "avail", + "the", + "it", + "even", + "though", + "say", + "receiv", + "thank", + "that", + "lot", + "thx", + "thnks", + "track", + "im", + "okay", + "use", + "voucher" + ], + "intents": [ + "cancel", + "confirm", + "delay", + "delivery", + "greetings.bye", + "greetings.hello", + "items", + "more.contact", + "more.help", + "order", + "payments", + "personal", + "profile.info", + "reason", + "refund.demanding", + "refund.status", + "thanks", + "track", + "user.response", + "voucher" + ], + "perceptrons": [ + [ + 1.0137546062469482, + -0.5474245548248291, + 11.848198890686035, + 0.4021919369697571, + 2.41121768951416, + 0.7352370619773865, + -0.11675096303224564, + -0.531909167766571, + 0.6789054870605469, + 0.5686358213424683, + -0.43452420830726624, + -0.7262206673622131, + -0.287818044424057, + -0.287818044424057, + -0.7318246364593506, + -0.9134994149208069, + -1.9920411109924316, + -1.386992335319519, + -0.287818044424057, + -1.0195549726486206, + -1.6521244049072266, + -0.287818044424057, + -0.002797139110043645, + -0.002797139110043645, + -0.18727074563503265, + -0.18727074563503265, + -2.59305739402771, + -0.8779183626174927, + -0.8779183626174927, + -0.33857086300849915, + -0.5329172015190125, + -0.14780335128307343, + -0.14780335128307343, + -0.1599469631910324, + -0.0766788199543953, + -0.0694824680685997, + -0.0694824680685997, + -0.0694824680685997, + -0.0694824680685997, + -0.0069275954738259315, + -0.010616364888846874, + -0.003003086894750595, + -0.16376909613609314, + -0.31498128175735474, + -0.006510923616588116, + -0.13119056820869446, + -0.006111734081059694, + -0.11673133820295334, + -0.11673133820295334, + -0.008423068560659885, + -0.0037213850300759077, + -0.0035652429796755314, + -0.0034156523179262877, + -0.012222626246511936, + -0.012222626246511936, + -0.02360852062702179, + -0.015696197748184204, + -0.0112577760592103, + -0.2867816090583801, + -0.17207609117031097, + 0, + 0, + -0.050361473113298416, + 0, + 0, + -0.3207390010356903, + -0.21179741621017456, + -0.11934908479452133, + -0.060127075761556625, + -0.24743472039699554, + -0.027676893398165703, + -0.5639355182647705, + -0.049367498606443405, + -0.015957951545715332, + -0.015957951545715332, + -0.015957951545715332, + -0.011720363982021809, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -0.4059183895587921, + -0.4059183895587921, + -0.2879806160926819, + -0.3107443153858185, + -0.3107443153858185, + 0, + 0, + -0.020473212003707886, + 0, + -0.011515682563185692, + -0.025052422657608986, + -0.5015026330947876, + -0.0068354010581970215, + -0.017309928312897682, + 0, + 0, + 0, + 0, + -0.38837194442749023, + -0.22747227549552917, + -0.22747227549552917, + 0, + 0, + -0.48755571246147156, + -0.48755571246147156, + -0.48755571246147156, + 0, + 0, + 0, + 0, + -0.005079725757241249, + -0.005079725757241249, + 0, + 0, + -2.282877206802368, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -2.269744634628296, + -0.018390238285064697, + 0, + 0, + 0, + -0.9896306948005769 + ], + [ + -0.3291322588920593, + -0.7270809412002563, + -0.16681955754756927, + -1.7737600803375244, + 1.892374873161316, + -1.1385151147842407, + -0.004768540151417255, + -0.19638314843177795, + -0.29744264483451843, + -0.18516650795936584, + 0.7383124828338623, + 4.340796947479248, + 0.2160470336675644, + 0.2160470336675644, + 2.533582925796509, + -0.36327800154685974, + 2.891287326812744, + -0.25634461641311646, + 0.09664863348007202, + 5.821985244750977, + -0.5967516303062439, + 0.2160470336675644, + 0.8364499807357788, + 0.6777428388595581, + 0.332731157541275, + 1.4811530113220215, + -0.5748860836029053, + -0.7142410278320312, + -0.7142410278320312, + -0.22726833820343018, + -0.18083734810352325, + -0.10491679608821869, + -0.10491679608821869, + -0.2062942087650299, + -0.036959417164325714, + -0.01146351732313633, + -0.01146351732313633, + -0.029890920966863632, + -0.01146351732313633, + -0.0765857994556427, + -0.1386977881193161, + -0.056668512523174286, + -0.061468705534935, + -0.4443903863430023, + -0.061468705534935, + -0.15229962766170502, + -0.057634614408016205, + -0.07866756618022919, + -0.07866756618022919, + -0.1323222666978836, + -0.0604797825217247, + -0.05799480155110359, + -0.055611882358789444, + -0.05049731209874153, + -0.10385537892580032, + -0.04838823154568672, + -0.04838823154568672, + -0.05117446184158325, + -0.1468285769224167, + -0.1599263697862625, + -0.03525151312351227, + -0.06675173342227936, + -0.3849853277206421, + -0.06461493670940399, + -0.05490032583475113, + -0.11226759105920792, + -0.11075426638126373, + -0.059033848345279694, + -0.03388472646474838, + -0.03388472646474838, + -0.03679174557328224, + -0.08090842515230179, + -0.03815352916717529, + -0.01093367300927639, + -0.01093367300927639, + -0.01093367300927639, + -0.32491371035575867, + -0.021362071856856346, + -0.021362071856856346, + -0.01930716447532177, + -0.01930716447532177, + -0.01930716447532177, + -0.02836797386407852, + -0.02836797386407852, + -0.023965725675225258, + -0.04677771031856537, + -0.06024671345949173, + -0.012021305970847607, + -0.012021305970847607, + -0.009854178875684738, + -0.06307084113359451, + -0.03795885667204857, + -0.013223793357610703, + -0.0717272013425827, + -0.0205734483897686, + -0.017550142481923103, + -0.0036168168298900127, + -0.006382063962519169, + -0.2861216962337494, + 0, + -0.0005231029936112463, + -0.038572512567043304, + 0, + 0, + 0, + -0.019087273627519608, + -0.00501098670065403, + -0.00501098670065403, + 0, + -0.26335811614990234, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -1.4128700494766235, + -0.005007071420550346, + -0.038572512567043304, + -0.038572512567043304, + -0.04391763359308243, + -0.04391763359308243, + -0.062341995537281036, + -1.1483964920043945, + -0.0012407215544953942, + 0, + 0, + -0.001188663300126791, + -0.0011387893464416265, + -1.2280935049057007, + 0, + 0, + 0, + 0, + -0.6626121500146844 + ], + [ + -1.2319387197494507, + -0.21801558136940002, + -7.187047004699707, + 3.2004880905151367, + 1.9386931657791138, + 3.482285976409912, + 1.7419606447219849, + -0.49997925758361816, + 2.0536139011383057, + -0.010287845507264137, + -0.7569913864135742, + -0.7583239674568176, + -0.7569913864135742, + -0.7569913864135742, + -0.7569913864135742, + 1.2748510837554932, + -1.373896598815918, + 4.428487300872803, + -0.7913482785224915, + -0.7583239674568176, + -2.4707539081573486, + -0.7569913864135742, + 0, + 0, + -0.19782285392284393, + 0, + 6.592715263366699, + -0.01434242632240057, + -0.01434242632240057, + 2.600883960723877, + -3.6173911094665527, + -0.23327256739139557, + -0.23327256739139557, + -0.2857179045677185, + -0.04939230531454086, + -0.6831585168838501, + -0.6831585168838501, + -0.6831585168838501, + -0.6831585168838501, + -0.04469833895564079, + -0.05349443107843399, + -0.019467800855636597, + -0.02620924822986126, + -0.21258683502674103, + -0.02620924822986126, + -1.9369317293167114, + -0.024292727932333946, + -0.04075866937637329, + -0.04075866937637329, + -0.06484147161245346, + -0.03467138111591339, + -0.03325444832444191, + -0.031895387917757034, + -0.13987219333648682, + -0.16497263312339783, + -0.02490754798054695, + -0.02490754798054695, + -0.031334955245256424, + -0.07237178832292557, + -0.07801632583141327, + -0.011419232003390789, + -0.01838001422584057, + -0.021844979375600815, + -0.024473845958709717, + -0.017832733690738678, + -0.04915530979633331, + -0.07258743047714233, + -0.040489088743925095, + -0.017853913828730583, + -0.017853913828730583, + -0.7192970514297485, + -0.7398727536201477, + -0.015225691720843315, + -0.008584280498325825, + -0.008584280498325825, + -0.008584280498325825, + -0.0018613251158967614, + -0.0004067723057232797, + -0.0004067723057232797, + -0.0012511666864156723, + -0.0012511666864156723, + -0.0012511666864156723, + -0.005174988880753517, + -0.005174988880753517, + -0.012096227146685123, + -0.012096227146685123, + -0.016754278913140297, + -0.010287845507264137, + -0.010287845507264137, + -0.008589562959969044, + -0.029535546898841858, + -0.017882922664284706, + -0.006336822174489498, + -0.046847932040691376, + -0.0018024882301688194, + -0.005290467292070389, + 0, + 0, + -1.9490653276443481, + -0.061430059373378754, + -0.061430059373378754, + -0.2684548497200012, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -0.20504964888095856, + -0.07669688761234283, + -0.07669688761234283, + -0.08714839071035385, + -0.014107950031757355, + -0.003805781714618206, + -0.012716507539153099, + -0.5159547328948975, + -0.5159547328948975, + -0.6215592622756958, + -1.8250330686569214, + -1.2078466415405273, + 0, + -0.2684548497200012, + -0.2684548497200012, + 0, + 0, + 0, + -0.19782285392284393, + 0, + 0, + 0, + 0, + 0, + -0.9307127594947815, + 0, + 0, + 0, + 0, + -2.8474070106071645 + ], + [ + -1.4451781511306763, + -1.0388836860656738, + -1.4264543056488037, + 2.5314676761627197, + -0.6839140057563782, + 0.7223742604255676, + -0.3522108495235443, + 1.6454493999481201, + 0.5822571516036987, + -0.03301115334033966, + -0.31011736392974854, + -0.31032857298851013, + -0.31011736392974854, + -0.31011736392974854, + -0.32225391268730164, + -0.8661167025566101, + 1.6880625486373901, + 1.8911114931106567, + -0.43638893961906433, + -0.32240062952041626, + -1.270650029182434, + -0.31011736392974854, + 0, + 0, + -0.354684978723526, + -0.006027175113558769, + -2.6551761627197266, + 2.678882122039795, + 2.678882122039795, + 2.1090545654296875, + 3.617849349975586, + 4.080210208892822, + 4.080210208892822, + 2.783381223678589, + 3.421110153198242, + 2.7887074947357178, + 2.7887074947357178, + 2.250455617904663, + 2.7887074947357178, + -0.17000629007816315, + -1.0011149644851685, + -0.852727472782135, + -0.12743763625621796, + -1.3179848194122314, + -0.08253855258226395, + -1.0258104801177979, + -0.07425964623689651, + -0.09686865657567978, + -0.09686865657567978, + -0.28232651948928833, + -0.13063865900039673, + -0.12527960538864136, + -0.12014026939868927, + -0.14789435267448425, + -0.23303095996379852, + -0.09936640411615372, + -0.4535828232765198, + -0.07006469368934631, + -0.19244204461574554, + -0.23298411071300507, + -0.04336852952837944, + -0.07129500061273575, + -0.3177240490913391, + -0.0663289874792099, + -0.05570872128009796, + -0.14094312489032745, + -0.23260855674743652, + -0.1286415457725525, + -0.05846906080842018, + -0.09133340418338776, + -0.5046393275260925, + -1.0630557537078857, + -0.34214919805526733, + -0.031061027199029922, + -0.031061027199029922, + -0.031061027199029922, + -0.3316175937652588, + -0.004776031244546175, + -0.004776031244546175, + -0.006242210045456886, + -0.006242210045456886, + -0.006242210045456886, + -0.019280698150396347, + -0.019280698150396347, + -0.05666345730423927, + -0.6110260486602783, + -0.6137350797653198, + -0.03301115334033966, + -0.03301115334033966, + -0.02883484587073326, + -0.23428231477737427, + -0.16950009763240814, + -0.0407194085419178, + -0.15757475793361664, + -0.21978570520877838, + -0.03889086842536926, + -0.11133536696434021, + -0.24443987011909485, + -1.0306564569473267, + -0.10143721848726273, + -0.20791855454444885, + -0.7177991271018982, + -0.3758620321750641, + 0, + 0, + -0.23245687782764435, + 0, + 0, + 0, + 0, + -0.5588673949241638, + -0.39160844683647156, + -0.39160844683647156, + -0.11280962079763412, + -0.01937008835375309, + -0.006116317119449377, + -0.02907666377723217, + -0.7320131063461304, + -0.7320131063461304, + -0.6825774312019348, + -0.6825774312019348, + -0.803561806678772, + 0, + -0.29712578654289246, + -0.29712578654289246, + 0, + 0, + 0, + -0.3424622714519501, + 0, + 0, + 0, + 0, + 0, + -0.40630239248275757, + 0, + 0, + 0, + 0, + -1.0805806744340831 + ], + [ + -2.1148478984832764, + 0.7997274994850159, + 0, + -0.8561583161354065, + -0.9215051531791687, + -0.006411668844521046, + -0.6620265245437622, + -1.2655911445617676, + -2.195646286010742, + -0.1171831414103508, + -0.22510455548763275, + -0.32071661949157715, + 0, + 0, + -0.22510455548763275, + -0.1251218467950821, + -0.439662367105484, + 0, + -0.23576650023460388, + -0.32071661949157715, + -2.3462860584259033, + 0, + -0.22510455548763275, + -0.4320996403694153, + -0.3351232707500458, + 0, + -1.3317266702651978, + 0, + 0, + -0.18428704142570496, + -0.7287682294845581, + -0.8036311864852905, + -0.8036311864852905, + 1.5954744815826416, + -0.4747324287891388, + 0, + 0, + -0.22864001989364624, + 0, + 12.440009117126465, + 7.522450923919678, + 3.414806365966797, + 5.261117458343506, + 0.5175203084945679, + 6.749449253082275, + 0.9009185433387756, + 4.107618808746338, + 5.865237712860107, + 5.865237712860107, + -1.7676278352737427, + -1.747532844543457, + -1.7450001239776611, + -1.7422480583190918, + -0.5406187176704407, + -0.6789042353630066, + -1.2395411729812622, + -0.739136278629303, + -0.18670904636383057, + -0.3578399419784546, + -2.0377581119537354, + -0.09841568022966385, + -0.12081167101860046, + -1.1265017986297607, + -0.22349581122398376, + -0.1958358734846115, + -0.5817500352859497, + -2.1600258350372314, + -1.4215301275253296, + -0.296112596988678, + -1.8322443962097168, + -0.6588131785392761, + -0.9206690192222595, + -0.4332333505153656, + -0.3052285313606262, + -0.3052285313606262, + -0.3052285313606262, + -0.02176925726234913, + 0, + 0, + -0.0050658793188631535, + -0.0050658793188631535, + -0.0050658793188631535, + -0.029020555317401886, + -0.029020555317401886, + -0.27671024203300476, + -0.533606231212616, + -1.04434072971344, + -0.1171831414103508, + -0.1171831414103508, + -0.13155850768089294, + -0.5340460538864136, + -0.3047577440738678, + -0.2095731496810913, + -1.2781013250350952, + -0.493476003408432, + -0.4744429588317871, + -0.0272076353430748, + -0.03713247552514076, + -0.35035839676856995, + -0.09408554434776306, + -0.12777751684188843, + -1.0055477619171143, + -0.9965164065361023, + -0.12271378934383392, + -0.12271378934383392, + -0.12974151968955994, + -0.12974151968955994, + -0.7104417681694031, + -0.5687134265899658, + 0, + -0.1271994262933731, + 0, + 0, + -0.11704851686954498, + 0, + -0.05229974165558815, + -0.04225518926978111, + -0.4482121765613556, + -0.4482121765613556, + -0.39033257961273193, + -1.3211712837219238, + -0.6426398158073425, + -0.08353639394044876, + -0.006411668844521046, + -0.006411668844521046, + -0.02102518081665039, + -0.02102518081665039, + -0.17535850405693054, + -0.3351232707500458, + -2.2644107341766357, + -0.04576003551483154, + 0, + -1.7263158559799194, + -1.72139310836792, + -0.8258929252624512, + -0.507616400718689, + -0.38756808638572693, + -0.00036949245259165764, + -0.00036949245259165764, + 1.7494741355394363 + ], + [ + -0.7732609510421753, + -4.034710884094238, + -0.16621102392673492, + -1.6832696199417114, + -2.2922849655151367, + -0.43388643860816956, + -1.0391435623168945, + -5.290063381195068, + -2.185589551925659, + -0.12314745038747787, + -0.5387074947357178, + -0.6298438906669617, + 0, + 0, + -0.5978250503540039, + 0, + -1.0414392948150635, + 0, + -0.31424424052238464, + -0.6889615654945374, + -2.4489927291870117, + 0, + -0.5387074947357178, + -1.0135843753814697, + -0.6875489354133606, + -0.04868263751268387, + -2.267845630645752, + 0, + 0, + -0.3452916443347931, + -1.842872977256775, + -0.07328616827726364, + -0.07328616827726364, + -1.2708898782730103, + -0.07328616827726364, + -0.01949692890048027, + -0.01949692890048027, + -0.638847827911377, + -0.01949692890048027, + -3.9341750144958496, + -1.613313913345337, + -1.1190882921218872, + -0.738145649433136, + -2.546537160873413, + -0.717369019985199, + -1.8951687812805176, + -0.4939974844455719, + -0.11324970424175262, + -0.11324970424175262, + 10.278204917907715, + 10.237865447998047, + 10.222160339355469, + 10.205157279968262, + 6.274806022644043, + 4.996850490570068, + 2.905292510986328, + 7.114895343780518, + -0.2540985643863678, + -1.758578896522522, + -0.7043660879135132, + -1.2804059982299805, + -1.3918099403381348, + -2.1373608112335205, + -0.1865386962890625, + -0.13705794513225555, + -0.44377002120018005, + -2.8761284351348877, + -0.4335267245769501, + -0.3696480691432953, + -0.3948061168193817, + -0.19673971831798553, + -1.0657670497894287, + -1.130800485610962, + -0.09097345173358917, + -0.09097345173358917, + -0.09097345173358917, + -4.737697601318359, + -0.07488127052783966, + -0.07488127052783966, + -0.07511845231056213, + -0.07511845231056213, + -0.07511845231056213, + -0.07152976095676422, + -0.07152976095676422, + -0.7411147356033325, + -1.3895823955535889, + -2.6020333766937256, + -0.12314745038747787, + -0.12314745038747787, + -0.10397472232580185, + -1.4408961534500122, + -0.725871205329895, + -0.6670722365379333, + -2.7231180667877197, + -0.3968248665332794, + -1.4507756233215332, + -0.20117171108722687, + -0.30739134550094604, + -1.123271107673645, + 0, + -0.1443576067686081, + -0.3079591393470764, + -0.05154714360833168, + -0.1468522697687149, + -0.1468522697687149, + -0.2189943641424179, + -0.2189943641424179, + -0.4288059175014496, + -0.17068028450012207, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -1.2483890056610107, + -1.2483890056610107, + -0.32805249094963074, + -0.7630698680877686, + -1.8030940294265747, + -0.4283504784107208, + -0.21113920211791992, + -0.21113920211791992, + -0.34558773040771484, + -0.34558773040771484, + -0.08313897997140884, + -0.6284115314483643, + -4.005587577819824, + -1.0675207376480103, + -0.5681915879249573, + -3.989133358001709, + -3.9799017906188965, + -1.730439305305481, + -6.760979175567627, + 0, + 0, + 0, + 4.00835038226357 + ], + [ + -2.0729150772094727, + 1.2105480432510376, + 0, + -3.075517416000366, + -2.1280879974365234, + 0, + -1.4291672706604004, + -3.7731683254241943, + 3.750875234603882, + -3.4637320041656494, + -3.808359146118164, + -0.7863858938217163, + -0.7863858938217163, + -0.7863858938217163, + -0.7863858938217163, + -0.7872745394706726, + -0.7872745394706726, + -0.8203457593917847, + 4.417398929595947, + -0.7863858938217163, + 7.2096943855285645, + -0.7863858938217163, + 0, + 0, + -0.0004443310899659991, + 0, + -1.4016138315200806, + 0, + 0, + -0.0339190810918808, + -0.5746974349021912, + 0, + 0, + -1.8838376998901367, + 0, + 0, + 0, + -0.29276347160339355, + 0, + 0, + -1.3986157178878784, + 0, + -2.1145453453063965, + 1.3252555131912231, + -0.3257300853729248, + 3.266697406768799, + -1.3986157178878784, + -0.37427330017089844, + -0.37427330017089844, + 0, + 0, + 0, + 0, + 0, + 1.3228527307510376, + -4.476612567901611, + 0, + 1.6962932348251343, + 2.854417085647583, + 5.147310733795166, + 1.3228527307510376, + 2.871696710586548, + -0.8401740193367004, + 2.4716386795043945, + 2.4736406803131104, + 8.386700630187988, + 3.6803982257843018, + 6.546629428863525, + 5.792974948883057, + 4.042235851287842, + 1.9097124338150024, + 0.2351648062467575, + 2.9972054958343506, + 0.014523402787744999, + 0.014523402787744999, + 0.014523402787744999, + 0.8547791838645935, + 2.3277828693389893, + 2.3277828693389893, + 1.5485361814498901, + 1.5485361814498901, + 1.5485361814498901, + 0.06981951743364334, + 0.06981951743364334, + -0.3465847671031952, + -0.7826536297798157, + -1.29860520362854, + -0.6727563142776489, + -0.6727563142776489, + -0.7746207118034363, + -1.5045040845870972, + -1.1596300601959229, + -0.21376065909862518, + -1.2060894966125488, + -0.7638314962387085, + -0.2846128046512604, + -0.21201957762241364, + -0.380899578332901, + -1.7838538885116577, + -0.3531716465950012, + -0.4074578583240509, + -1.8838376998901367, + -1.8838376998901367, + -2.419515371322632, + -2.419515371322632, + -1.6732022762298584, + -0.5018000602722168, + -1.5228439569473267, + -0.8719638586044312, + -3.0196237564086914, + -0.23078517615795135, + 0, + 0, + -0.19459597766399384, + 0, + -0.06361047178506851, + -0.032615043222904205, + -0.06359861046075821, + -0.06359861046075821, + -0.5170090794563293, + -1.4016138315200806, + -2.525960922241211, + -0.01885991171002388, + 0, + 0, + 0, + 0, + 0, + -0.0004443310899659991, + -1.074087142944336, + 0, + -0.08746566623449326, + -0.0025967436376959085, + -0.002491012681275606, + 0, + -0.1930888444185257, + -4.403278350830078, + -0.8553791642189026, + -0.8553791642189026, + -0.4586347245196692 + ], + [ + -0.22140660881996155, + -0.2870965600013733, + 0, + -0.7846165299415588, + -0.15873953700065613, + -0.011281087063252926, + -2.0644774436950684, + -0.23955339193344116, + -0.19472968578338623, + -0.08523216098546982, + -0.10342646390199661, + -0.05132650211453438, + 0, + 0, + -0.025844065472483635, + -0.00017023066175170243, + -0.11176835745573044, + 0, + 0, + -0.05132650211453438, + -0.12461326271295547, + 0, + -0.025844065472483635, + -0.08902999758720398, + -0.06705322861671448, + 0, + -0.15028099715709686, + 0, + 0, + -0.0490955151617527, + -0.745243489742279, + -0.0033512338995933533, + -0.0033512338995933533, + -0.12648093700408936, + -0.0033512338995933533, + -0.6030545830726624, + -0.6030545830726624, + 3.2248520851135254, + -0.6030545830726624, + -0.12146465480327606, + -0.03870086744427681, + -0.03870086744427681, + 0, + -0.30469033122062683, + 0, + -1.3063645362854004, + 0, + 0, + 0, + -0.137643501162529, + -0.11902635544538498, + -0.1179807186126709, + -0.11715350300073624, + 0, + 0, + -0.1290770322084427, + -0.05281056836247444, + 0, + -0.002628645393997431, + 0, + 0, + 0, + -0.23906093835830688, + 0, + 0, + 0, + -0.0207328200340271, + 0, + 0, + 0, + -0.014595159329473972, + -0.27468565106391907, + -0.07625328004360199, + 0, + 0, + 0, + -0.095185287296772, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3.235283613204956, + 7.06290864944458, + 3.877683401107788, + -0.06153886765241623, + -0.06153886765241623, + -0.05395161360502243, + -0.2852313220500946, + -0.17201417684555054, + -0.08172166347503662, + -1.455437421798706, + -1.2979955673217773, + -0.053663939237594604, + -0.051299452781677246, + -0.07426265627145767, + -0.18414506316184998, + -0.014595159329473972, + -0.038418881595134735, + -0.08742590248584747, + -0.06441590934991837, + -0.0632362961769104, + -0.0632362961769104, + -0.052749164402484894, + -0.03576807305216789, + -0.10305000096559525, + -0.0530945248901844, + -0.06034179404377937, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -0.0791553482413292, + -0.0791553482413292, + -0.04793284460902214, + -0.06975246965885162, + -0.23104454576969147, + -0.0613517090678215, + -0.011281087063252926, + -0.011281087063252926, + -0.011696902103722095, + -0.011696902103722095, + -0.03297434374690056, + -0.06705322861671448, + -0.21499337255954742, + -1.9282985925674438, + -0.0005038426024839282, + -0.14002913236618042, + -0.13676577806472778, + -0.0662107840180397, + -0.08920516818761826, + -0.009708352386951447, + 0, + 0, + 0.11517137685731398 + ], + [ + 3.200519561767578, + -0.7589099407196045, + -1.5275399684906006, + -3.0995192527770996, + -2.00679612159729, + 0, + -1.429031491279602, + -0.012760818004608154, + -1.0567845106124878, + 0.7500213384628296, + -0.299394428730011, + -0.07060258835554123, + 0, + 0, + -0.0594392791390419, + 0, + -0.12525728344917297, + 0, + 0, + -0.07060258835554123, + -1.4175167083740234, + 0, + -0.0594392791390419, + -0.0651191994547844, + -0.11094554513692856, + 0, + -0.9104652404785156, + 0, + 0, + -0.00293157109990716, + -0.5573859810829163, + -0.08501628041267395, + -0.08501628041267395, + -0.3801807165145874, + -0.05955946817994118, + 0, + 0, + -0.552058219909668, + 0, + -0.3559640347957611, + -0.7074311971664429, + -0.023821957409381866, + -0.042731598019599915, + -1.234588623046875, + 0, + 1.0346851348876953, + -0.6757804155349731, + 0, + 0, + -0.38050055503845215, + -0.3475361168384552, + -0.3460211753845215, + -0.3452502191066742, + 0, + 0, + -0.3270740211009979, + -0.35466599464416504, + 0, + -1.6785608530044556, + -0.042731598019599915, + 0, + 0, + -0.8738346695899963, + 0, + 0, + -1.3493611812591553, + -0.33822640776634216, + -0.2678961753845215, + 0, + -0.042731598019599915, + -3.1001944541931152, + 2.860347270965576, + 1.7957031726837158, + -0.018584920093417168, + -0.018584920093417168, + -0.018584920093417168, + -0.5774023532867432, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -0.5536002516746521, + -1.1061232089996338, + 1.3213735818862915, + 3.957881212234497, + 3.957881212234497, + 4.638659954071045, + 4.42684268951416, + 2.3681249618530273, + 2.0528454780578613, + 7.3816752433776855, + 4.17827033996582, + 6.468132495880127, + 4.017130374908447, + 5.217040538787842, + -1.9796524047851562, + -0.3908320665359497, + -1.6709413528442383, + -0.19154834747314453, + -0.19154834747314453, + -0.17416070401668549, + -0.17416070401668549, + -1.2428146600723267, + -0.7238667011260986, + -0.8973953723907471, + -0.0867948979139328, + -0.16916145384311676, + -2.2424511909484863, + -0.32075998187065125, + -0.32075998187065125, + -0.2476712465286255, + -1.478501558303833, + 0, + -0.006954232230782509, + 0, + 0, + -0.3725646734237671, + -0.7989145517349243, + -1.854297399520874, + -0.016981689259409904, + 0, + 0, + 0, + 0, + -0.00293157109990716, + -0.11094554513692856, + -0.6883361339569092, + -0.3473883867263794, + -0.3317181169986725, + -0.4416748285293579, + -0.43391868472099304, + -0.1418841928243637, + -0.27213436365127563, + -0.021505234763026237, + -2.5270771980285645, + -2.5270771980285645, + 0.3450876493306147 + ], + [ + -0.6420600414276123, + -1.9181957244873047, + -0.9622277617454529, + 1.3733851909637451, + 1.7130494117736816, + -2.1014411449432373, + 0.9902098774909973, + 0.909596860408783, + -1.1616764068603516, + -0.0607258602976799, + -0.4026883542537689, + -0.10765376687049866, + -0.0967993512749672, + -0.0967993512749672, + -0.2516874670982361, + -0.5531376600265503, + -0.9255282282829285, + -0.5531376600265503, + -0.0967993512749672, + -0.2632655203342438, + 3.4905331134796143, + -0.0967993512749672, + 0, + -2.0475926399230957, + -0.12525439262390137, + -0.12525439262390137, + -0.9677612781524658, + -0.021905967965722084, + -0.021905967965722084, + -1.255740761756897, + -0.11220162361860275, + 0, + 0, + -0.04746983200311661, + 0, + -0.07886837422847748, + -0.07886837422847748, + -0.07886837422847748, + -0.07886837422847748, + 0, + 0, + 0, + 0, + -1.0360527038574219, + 0, + -0.02445678412914276, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -0.28641000390052795, + 0, + -0.21070648729801178, + 0, + -0.47402000427246094, + 0, + -0.28641000390052795, + -0.3845341205596924, + -1.04691481590271, + -0.2858976721763611, + -0.28032997250556946, + 0, + 0, + 0, + 0, + 0, + -0.9096251130104065, + 1.1884770393371582, + -0.6546885967254639, + 0, + 0, + 0, + -0.7524798512458801, + -0.10243398696184158, + -0.10243398696184158, + -0.09811604022979736, + -0.09811604022979736, + -0.09811604022979736, + 0, + 0, + 0, + 0, + -0.015519799664616585, + 0, + 0, + 0, + -0.3532436788082123, + -0.3532436788082123, + 0, + -0.3316343426704407, + -0.3924787938594818, + 0, + -0.2033385932445526, + -0.24257385730743408, + 7.729468822479248, + 2.36399245262146, + 5.470393657684326, + -0.6171549558639526, + -0.04746983200311661, + -0.06629005819559097, + -0.06629005819559097, + -0.2993519902229309, + -0.10318173468112946, + -0.1877218633890152, + -0.05038710683584213, + -0.2762553095817566, + -1.657719612121582, + -0.20799502730369568, + -0.20799502730369568, + -0.8902863264083862, + -0.3316343426704407, + -0.32283616065979004, + -0.3706023395061493, + -0.5664393901824951, + -0.5664393901824951, + -0.015637066215276718, + -0.02445678412914276, + -2.798739433288574, + 0, + -0.5251821875572205, + -0.5251821875572205, + -1.4305318593978882, + -1.4305318593978882, + -0.6157680153846741, + 0, + 0, + -0.015519799664616585, + 0, + 0, + 0, + -1.0123201608657837, + 0, + 0, + -0.39987918734550476, + -0.39987918734550476, + -1.012125551662553 + ], + [ + -3.8618507385253906, + 2.661921739578247, + -0.324486643075943, + -2.681408166885376, + -1.070864200592041, + -0.1429082453250885, + -0.9401305913925171, + -2.0808780193328857, + 3.4362409114837646, + -0.5510810613632202, + 5.012321949005127, + -1.0682185888290405, + -0.24958382546901703, + -0.24958382546901703, + -1.0682185888290405, + -0.24958382546901703, + -0.5058361291885376, + -1.057460904121399, + -1.2256693840026855, + -1.0682185888290405, + -0.5766453146934509, + -0.24958382546901703, + -0.7393555045127869, + -0.7923861742019653, + -0.02366284839808941, + 0, + -0.5207908749580383, + 0, + 0, + -0.9092050790786743, + -1.1664316654205322, + -0.23604610562324524, + -0.23604610562324524, + 2.949445962905884, + -0.22940532863140106, + 0, + 0, + 0, + 0, + -0.10126614570617676, + -1.332739233970642, + -1.332739233970642, + -0.5901528000831604, + -0.7608097195625305, + -0.5048365592956543, + -0.5548068881034851, + 0, + -1.1728324890136719, + -1.1728324890136719, + -0.06950733810663223, + -0.04147882014513016, + -0.03993690013885498, + -0.03845171630382538, + 0, + -0.10618571937084198, + -1.829228401184082, + -0.6239013671875, + -0.7514669299125671, + -2.0999088287353516, + -1.7867448329925537, + -0.10618571937084198, + -0.9889819622039795, + 0.08560609817504883, + -2.1254465579986572, + -1.9958056211471558, + -0.3938402533531189, + -1.6643561124801636, + -0.16029290854930878, + -0.31657809019088745, + -0.39398059248924255, + -0.7145650386810303, + -0.7145650386810303, + -0.6206731796264648, + -0.007626057136803865, + -0.007626057136803865, + -0.007626057136803865, + 3.9068644046783447, + -0.7097881436347961, + -0.7097881436347961, + -0.8330930471420288, + -0.8330930471420288, + -0.8330930471420288, + -0.13839125633239746, + -0.13839125633239746, + 0, + 0, + -0.08971934020519257, + 0, + 0, + -0.2813188433647156, + -0.2813188433647156, + -0.2813188433647156, + 0, + -0.09263147413730621, + 0, + 0, + 0, + 0, + -0.19597065448760986, + 0, + 0, + 4.378785133361816, + 4.526092529296875, + 5.922995090484619, + 5.922995090484619, + 10.107356071472168, + -1.0512547492980957, + 6.942415714263916, + 7.998384952545166, + 6.079689025878906, + -0.7038584351539612, + -0.15375125408172607, + -0.15375125408172607, + -0.23844508826732635, + -0.09263147413730621, + -0.05494779720902443, + -0.048433784395456314, + -0.09314513951539993, + -0.09314513951539993, + -0.23382462561130524, + -0.35334011912345886, + -1.2957956790924072, + -0.409479022026062, + -0.1429082453250885, + -0.1429082453250885, + -0.009866238571703434, + -0.009866238571703434, + -0.023210253566503525, + -0.02366284839808941, + -1.1075395345687866, + -0.08971934020519257, + -0.022661320865154266, + -0.2660955786705017, + -0.2567301392555237, + -0.18965840339660645, + -1.3401103019714355, + -0.6953500509262085, + -0.5650429129600525, + -0.5650429129600525, + -0.2684726967987179 + ], + [ + -1.0223896503448486, + -3.3254759311676025, + -0.3594515919685364, + -1.2034928798675537, + -0.7094810009002686, + -0.11835822463035583, + 3.11519455909729, + 7.990487098693848, + 0.09484227001667023, + -0.03216062858700752, + -0.9383915066719055, + 0, + 0, + 0, + -0.043270353227853775, + 0, + -0.18485020101070404, + 0, + -0.6258415579795837, + -0.043270353227853775, + -2.782179832458496, + 0, + 0, + -0.12773528695106506, + -0.06305236369371414, + -0.043270353227853775, + -0.24601295590400696, + 0, + 0, + -0.10957992821931839, + -0.8636319041252136, + -1.4621272087097168, + -1.4621272087097168, + -1.591508150100708, + -0.7966815233230591, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -0.6140099167823792, + 3.904226064682007, + -0.6140099167823792, + -0.17317022383213043, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -0.2878693640232086, + -0.3986928164958954, + -0.9750335216522217, + 3.525062322616577, + -0.35377830266952515, + -1.0733240842819214, + -1.0409214496612549, + -0.06699375808238983, + -0.6057316064834595, + 5.004830837249756, + -0.3178902864456177, + -0.25369635224342346, + 0, + 0, + 0, + 0, + 0, + -0.13478565216064453, + -3.095573663711548, + -1.419830322265625, + 0, + 0, + 0, + 2.3318679332733154, + -0.4985043406486511, + -0.4985043406486511, + -0.4953354299068451, + -0.4953354299068451, + -0.4953354299068451, + -0.16272452473640442, + -0.16272452473640442, + 0, + 0, + -0.37520232796669006, + 0, + 0, + 0, + -0.7256875038146973, + -0.7256875038146973, + 0, + -0.015781344845891, + -0.7694862484931946, + 0, + -0.377000093460083, + -0.4205278754234314, + -1.2554208040237427, + 0, + -0.9600287079811096, + -0.24299727380275726, + -0.11906661838293076, + -0.7379854321479797, + -0.7379854321479797, + -2.122434139251709, + 0, + -3.112035036087036, + -3.112035036087036, + -0.9383915066719055, + -0.4391774535179138, + -0.31684646010398865, + -0.31684646010398865, + -0.05564532428979874, + -0.015781344845891, + -0.005053800996392965, + -0.009785249829292297, + -0.03709046170115471, + -0.03709046170115471, + -0.09168491512537003, + -0.17317022383213043, + -1.3464040756225586, + -0.9806076288223267, + -0.08754246681928635, + -0.08754246681928635, + -0.10532320290803909, + -0.10532320290803909, + -0.006247076205909252, + -0.011304006911814213, + -1.222589373588562, + -0.37520232796669006, + -0.10687950998544693, + -0.0387471504509449, + -0.03715534880757332, + -0.22339007258415222, + -0.1271572709083557, + 0, + -0.8067957758903503, + -0.8067957758903503, + -2.6753750922540664 + ], + [ + -0.12718820571899414, + -0.9853842854499817, + -0.5445895195007324, + 5.641761779785156, + -4.2250847816467285, + -0.280537873506546, + -0.4256134331226349, + 0.3338262736797333, + -0.918182909488678, + -1.3661450147628784, + 0, + 0, + 0, + 0, + 0, + -0.6540312767028809, + -0.6540312767028809, + -0.8484894037246704, + 0, + 0, + 0, + 0, + 0, + 0, + -0.6540312767028809, + 0, + -0.5487508773803711, + 0, + 0, + -0.8484894037246704, + -2.1415629386901855, + -0.0575675331056118, + -0.0575675331056118, + -0.0575675331056118, + -0.0575675331056118, + -0.8333489298820496, + -0.8333489298820496, + -0.8333489298820496, + -0.8333489298820496, + 0, + 0, + 0, + 0, + -0.1419188380241394, + 0, + -0.4246017634868622, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -0.15350814163684845, + 0, + 0, + -1.3661450147628784, + 0, + 0, + 0, + -0.037509985268116, + 0, + 0, + -0.844822108745575, + -0.22552567720413208, + 0, + 0, + 0, + 1.1137510538101196, + 0.27898016571998596, + -1.4447450637817383, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -0.11407973617315292, + 0, + 0, + 0, + -0.5878565311431885, + -0.3544137477874756, + -0.21363283693790436, + 0.967481255531311, + -0.3053710162639618, + -0.7675119638442993, + -0.007795386016368866, + -0.007795386016368866, + -2.1931285858154297, + -2.1931285858154297, + -2.1931285858154297, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5.353912830352783, + 2.1121280193328857, + 2.1121280193328857, + 3.23943829536438, + 2.038325786590576, + 2.9767329692840576, + 5.013604164123535, + 4.2997145652771, + 4.2997145652771, + -0.19218771159648895, + -0.3588467240333557, + -3.047213554382324, + 0, + 0, + 0, + 0, + 0, + 0, + -0.6540312767028809, + -0.250901460647583, + -0.08072638511657715, + -0.021549042314291, + -0.09487134218215942, + -0.09106424450874329, + -2.8172950744628906, + -0.0021756167989224195, + -0.04602548107504845, + -0.4669470489025116, + -0.4669470489025116, + -0.14674373041027708 + ], + [ + -0.23209355771541595, + -1.1615275144577026, + -0.005190588533878326, + -3.6277239322662354, + -3.169827938079834, + -1.4822739362716675, + -1.7275220155715942, + -1.2139179706573486, + -0.2021854668855667, + -0.1523972898721695, + -0.11314810812473297, + -0.08779977262020111, + 0, + 0, + -0.059583138674497604, + -1.5408085584640503, + -0.15550094842910767, + -1.6674295663833618, + 0, + -0.08779977262020111, + -0.05608239024877548, + 0, + -0.059583138674497604, + -0.16862013936042786, + -0.045018356293439865, + 0, + 9.606298446655273, + 0, + 0, + -0.24552878737449646, + 4.0383477210998535, + -1.0773080587387085, + -1.0773080587387085, + -1.092512607574463, + 0, + -0.3511105179786682, + -0.3511105179786682, + -0.3511105179786682, + -0.3511105179786682, + -0.4085167646408081, + -0.9077059030532837, + -0.007602308411151171, + -0.09288404881954193, + -0.23860332369804382, + -0.09288404881954193, + 1.0059305429458618, + -0.8925086259841919, + 0, + 0, + -0.41859158873558044, + -0.4048844575881958, + -0.4027630090713501, + -0.4005860686302185, + 0, + -0.0000060307379499136005, + -0.3426108956336975, + -0.0742335394024849, + 0, + -0.19533050060272217, + 0, + -0.0000060307379499136005, + -0.03409332409501076, + -0.1738254725933075, + 0, + 0, + 0, + -0.5821152925491333, + -0.4939543306827545, + 0, + 0, + -0.09052671492099762, + -0.20556989312171936, + -0.14422670006752014, + -0.15840737521648407, + -0.15840737521648407, + -0.15840737521648407, + -0.05607032775878906, + 0, + 0, + -0.03408125042915344, + -0.03408125042915344, + -0.03408125042915344, + 0, + 0, + -0.00014428634312935174, + -0.00014428634312935174, + -0.7335526943206787, + 0, + 0, + 0, + -0.004638887010514736, + -0.001708140131086111, + -0.001953831408172846, + -0.6299360990524292, + -0.6226332187652588, + 0, + 0, + 0, + -0.1530466079711914, + 0, + 0, + -0.04347405955195427, + 0, + 0, + 0, + 0, + 0, + -0.0043099685572087765, + -0.0043099685572087765, + -0.02191714569926262, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -3.760802745819092, + 3.084120035171509, + -0.6946653127670288, + -0.08140380680561066, + -0.04347405955195427, + -0.04347405955195427, + -0.034206993877887726, + -0.034206993877887726, + -0.02565651200711727, + -0.045018356293439865, + -0.42496785521507263, + -0.07680274546146393, + -0.033271674066782, + -0.4210048317909241, + -0.41981253027915955, + -0.11387768387794495, + -0.16938365995883942, + -0.012891450896859169, + -0.08184230327606201, + -0.08184230327606201, + 0.42938872349061835 + ], + [ + 1.292214274406433, + 0.26029807329177856, + -3.265094041824341, + 1.323584794998169, + 2.0901131629943848, + -0.3446880578994751, + -1.5694751739501953, + -0.8493215441703796, + -0.5331605672836304, + 1.0180702209472656, + -0.0004536763299256563, + 0, + 0, + 0, + 0, + -1.6404364109039307, + -3.4016611576080322, + -0.037170104682445526, + 0, + 0, + -1.677952527999878, + 0, + 0, + -2.535099506378174, + -2.8807036876678467, + 0, + -0.037170104682445526, + 0, + 0, + -2.04948091506958, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -0.0057027386501431465, + -0.5997958779335022, + 0, + 0, + 0, + -0.05450645834207535, + -0.05450645834207535, + 0, + 0, + 0, + 0, + 0, + -0.7775706052780151, + -0.4098859131336212, + 0, + -0.4394400715827942, + 2.708770275115967, + -0.4485231637954712, + -0.7775706052780151, + -0.785632848739624, + 1.7545942068099976, + 0, + 0, + -0.398097425699234, + -0.5929336547851562, + 0, + 0, + -0.0057027386501431465, + 1.2487276792526245, + -0.1948939859867096, + -0.8948578238487244, + 0, + 0, + 0, + -0.7649872899055481, + -0.7560442090034485, + -0.7560442090034485, + -0.003941982984542847, + -0.003941982984542847, + -0.003941982984542847, + 0, + 0, + 0, + 0, + -0.015651213005185127, + -1.3101164102554321, + -1.3101164102554321, + -0.26000744104385376, + -0.26000744104385376, + -0.26000744104385376, + 0, + -0.13279704749584198, + 0, + 0, + 0, + 0, + -3.0560569763183594, + 0, + 0, + -0.3446880578994751, + 0, + 0, + 0, + -0.21462611854076385, + -0.21462611854076385, + -0.26094186305999756, + -0.039530932903289795, + -0.0004536763299256563, + -0.6876921057701111, + -0.10246691852807999, + -0.10246691852807999, + -0.5645003914833069, + -0.13279704749584198, + -0.25327029824256897, + -0.37001025676727295, + -0.4633078873157501, + -0.4633078873157501, + 0, + 0, + 10.846235275268555, + 3.3832929134368896, + -0.3446880578994751, + -0.3446880578994751, + -1.0060052871704102, + -1.0060052871704102, + -1.5285238027572632, + -2.8807036876678467, + -0.17504888772964478, + -0.015651213005185127, + -0.0056052375584840775, + -0.0669519379734993, + -0.06419989466667175, + -3.2434446811676025, + -0.012034664861857891, + -0.15987606346607208, + -0.5193120241165161, + -0.5193120241165161, + -1.6741036713965793 + ], + [ + -1.2913458347320557, + -1.1410175561904907, + -0.1504516303539276, + -0.4215856194496155, + -2.5707266330718994, + -0.003674748819321394, + 0.13974682986736298, + -0.6987276077270508, + -0.39745214581489563, + -1.0970606803894043, + -0.3989872634410858, + -0.5193899273872375, + -0.08799205720424652, + -0.08799205720424652, + -1.1870545148849487, + 1.5699361562728882, + 3.095979928970337, + -0.3248724043369293, + -0.08799205720424652, + -1.2793129682540894, + -0.37088605761528015, + -0.08799205720424652, + -0.23400215804576874, + 3.2401087284088135, + 3.209850788116455, + -0.7191513776779175, + -0.016282057389616966, + -0.31694793701171875, + -0.31694793701171875, + 2.076730966567993, + -0.18918496370315552, + -0.013376947492361069, + -0.013376947492361069, + -0.04278998076915741, + -0.004762548021972179, + -0.011217543855309486, + -0.011217543855309486, + -0.017532918602228165, + -0.011217543855309486, + -0.028222307562828064, + -0.0557534284889698, + -0.02368752472102642, + -0.014544656500220299, + -0.17036139965057373, + -0.014544656500220299, + -0.02665693685412407, + -0.02205268293619156, + -0.0021749138832092285, + -0.0021749138832092285, + -0.049194373190402985, + -0.023485533893108368, + -0.02252129279077053, + -0.021596623584628105, + -0.10593338310718536, + -0.11573706567287445, + -0.3629792332649231, + -0.0001258983975276351, + 0, + -1.1090625524520874, + 0, + -0.0036658381577581167, + -0.006373447831720114, + -2.0295188426971436, + 0, + 0, + 0, + -0.2419360876083374, + 0, + 0, + 0, + -0.722599983215332, + -1.3000450134277344, + -0.33351635932922363, + 0, + 0, + 0, + -0.00553872948512435, + -0.0023433116730302572, + -0.0023433116730302572, + -0.0008746905950829387, + -0.0008746905950829387, + -0.0008746905950829387, + 0, + 0, + -0.0038404290098696947, + -0.006467245519161224, + -0.29648056626319885, + 0, + 0, + 0, + -0.03231830894947052, + -0.012083077803254128, + -0.014325657859444618, + -0.050506096333265305, + 0, + -0.012288431636989117, + 0, + 0, + 3.2137577533721924, + -0.13101829588413239, + -0.6130020618438721, + 0.572363018989563, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -0.001087351469323039, + 4.734946250915527, + -2.0157506465911865, + 0.572363018989563, + 0.572363018989563, + 1.8228611946105957, + 1.8228611946105957, + 1.7091647386550903, + 3.9290292263031006, + -0.5727516412734985, + -0.26853373646736145, + -0.18373097479343414, + -0.12631776928901672, + -0.12113834172487259, + -0.5061092376708984, + -0.13984650373458862, + -0.0468229278922081, + -0.09297265112400055, + -0.09297265112400055, + -0.6886503062546867 + ], + [ + -0.5077318549156189, + -1.8987878561019897, + -0.35498031973838806, + -1.8763723373413086, + -2.0653798580169678, + -0.9951695799827576, + 1.7244404554367065, + -2.168811082839966, + -2.1415905952453613, + 0, + -0.23866921663284302, + -0.3075999617576599, + 0, + 0, + -0.6796745657920837, + 0, + -0.891762912273407, + 0, + -0.40114548802375793, + -0.7481383681297302, + -0.7970276474952698, + 0, + -0.23866921663284302, + -1.0001347064971924, + -0.5179142355918884, + -0.36288952827453613, + -1.3573659658432007, + -0.09471361339092255, + -0.09471361339092255, + -0.158460795879364, + -1.3037259578704834, + -0.24867312610149384, + -0.24867312610149384, + -0.9490104913711548, + -0.09194017201662064, + -0.10955855250358582, + -0.10955855250358582, + -1.783765196800232, + -0.10955855250358582, + -2.646313428878784, + -1.0704121589660645, + -0.6563863158226013, + -1.646806240081787, + 0.34065505862236023, + -1.646806240081787, + -1.748129963874817, + -0.3809452950954437, + -0.3920825123786926, + -0.3920825123786926, + -2.667757511138916, + -2.6342594623565674, + -2.626725673675537, + -2.6188108921051025, + -2.1725428104400635, + -2.231743335723877, + -1.5861741304397583, + -1.1299805641174316, + -0.2543991804122925, + -0.36038318276405334, + -0.6977182626724243, + -0.04082721844315529, + -0.0990259125828743, + -1.3207709789276123, + -0.32789477705955505, + -0.3065217435359955, + -0.04539057984948158, + -0.698974609375, + 0, + -0.035125356167554855, + -0.035125356167554855, + -0.22145676612854004, + -0.8647521138191223, + -1.1884366273880005, + 0, + 0, + 0, + -1.1018884181976318, + -0.02143898233771324, + -0.02143898233771324, + -0.04176821559667587, + -0.04176821559667587, + -0.04176821559667587, + -0.08753964304924011, + -0.08753964304924011, + -1.7882267236709595, + -3.4442903995513916, + 2.565450668334961, + 0, + 0, + 0, + -0.4173498749732971, + -0.18671193718910217, + -0.22115515172481537, + -2.18947696685791, + -1.294816017150879, + -0.47465768456459045, + -0.033020079135894775, + -0.033020079135894775, + -0.8756087422370911, + 0, + 0, + -0.1833024024963379, + -0.016651319339871407, + -0.5369012951850891, + -0.5369012951850891, + 0, + 0, + -0.9610477089881897, + -0.9610477089881897, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -0.3945724070072174, + -0.3945724070072174, + 0, + -0.03544429689645767, + -1.1593499183654785, + -0.18549960851669312, + -0.158460795879364, + -0.158460795879364, + -0.6906583905220032, + -0.6906583905220032, + 0, + -0.097763292491436, + 11.378676414489746, + 7.304425239562988, + -0.2728983163833618, + 11.550247192382812, + 11.530162811279297, + -0.7071039080619812, + -1.0914838314056396, + -0.2598259449005127, + -0.7921883463859558, + -0.7921883463859558, + 2.6784531290209324 + ], + [ + 0.380982369184494, + -0.3258844316005707, + -3.4917116165161133, + -0.27739769220352173, + 3.301130533218384, + -1.073818564414978, + -1.505022644996643, + 0.27774229645729065, + -1.3573098182678223, + -0.5270810127258301, + -0.24048076570034027, + -0.8727750778198242, + -0.24048076570034027, + -0.24048076570034027, + -0.3888031840324402, + -0.9459995627403259, + -1.035170555114746, + -0.9459995627403259, + -0.24048076570034027, + -0.9452674388885498, + -0.6623518466949463, + -0.24048076570034027, + 0, + 0, + -0.05696911737322807, + -0.05696911737322807, + -1.082931637763977, + -0.062191810458898544, + -0.062191810458898544, + 0, + -0.09859281778335571, + -0.08082381635904312, + -0.08082381635904312, + -0.08082381635904312, + -0.04772651940584183, + -0.053415942937135696, + -0.053415942937135696, + -0.053415942937135696, + -0.053415942937135696, + 0, + 0, + 0, + -0.005526010878384113, + -0.12121487408876419, + 0, + 0, + 0, + -0.007201648782938719, + -0.007201648782938719, + 0, + 0, + 0, + 0, + 0, + 0, + -0.7524583339691162, + 0, + 0, + 0, + -0.005526010878384113, + 0, + 0, + -0.11378240585327148, + 0, + 0, + -0.2490343451499939, + -0.8400887846946716, + 0, + 0, + -0.005526010878384113, + 0.6075114607810974, + -0.014191977679729462, + -0.8784146904945374, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -0.12548333406448364, + -0.12548333406448364, + -0.020310726016759872, + -0.2117539495229721, + -0.2117539495229721, + 0, + -0.20880654454231262, + -0.1780262440443039, + 0, + 0, + 0, + -0.756860613822937, + -0.37837353348731995, + -0.37837353348731995, + 0, + 0, + 0, + 0, + -0.026584208011627197, + -0.026584208011627197, + -0.026584208011627197, + 0, + 0, + -0.8632755875587463, + -0.09813888370990753, + -0.09813888370990753, + -0.5334068536758423, + -0.20880654454231262, + -0.141290083527565, + -0.18400461971759796, + -0.24847225844860077, + -0.24847225844860077, + 0, + 0, + -2.349536180496216, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11.706945419311523, + -0.16522370278835297, + -0.22235870361328125, + -0.5889762043952942, + -0.5889762043952942, + -0.669537763085042 + ], + [ + -1.257106065750122, + 1.9722994565963745, + -0.7403843998908997, + -1.3834630250930786, + -0.97077476978302, + -0.05542337894439697, + -0.2643361985683441, + -1.355240821838379, + 0.2562214136123657, + -0.35549765825271606, + -0.2758129835128784, + -0.3064761757850647, + -0.10345030575990677, + -0.10345030575990677, + -0.2758129835128784, + -0.10345030575990677, + -0.16006582975387573, + -0.7584336996078491, + -0.2175266444683075, + -0.3064761757850647, + -0.5171506404876709, + -0.10345030575990677, + -0.09477495402097702, + -0.09477495402097702, + 0, + 0, + 0, + 0, + 0, + -0.5531495213508606, + -0.6761674284934998, + -0.16182109713554382, + -0.16182109713554382, + -0.36729753017425537, + -0.06748000532388687, + -0.008540223352611065, + -0.008540223352611065, + -0.026633432134985924, + -0.008540223352611065, + -0.3734872341156006, + -0.313742458820343, + -0.1641637533903122, + -0.1652340143918991, + -0.5971291661262512, + -0.1652340143918991, + -3.7121341228485107, + -0.09692328423261642, + -0.8412911891937256, + -0.8412911891937256, + -0.47956082224845886, + -0.3054158389568329, + -0.29459866881370544, + -0.2841568887233734, + -0.1532236486673355, + -0.19646738469600677, + 7.023808479309082, + -6.51149845123291, + -0.12688302993774414, + -0.3361934423446655, + -0.17926666140556335, + -0.026485782116651535, + -0.026485782116651535, + -0.0460079051554203, + -0.07561150193214417, + -0.05101285129785538, + -0.18610434234142303, + 5.773743629455566, + -3.558574914932251, + -3.6353445053100586, + -3.6353445053100586, + -0.19886760413646698, + -0.45692336559295654, + -0.6680894494056702, + -0.5418877601623535, + -0.5418877601623535, + -0.5418877601623535, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -0.014895623549818993, + -0.03276222199201584, + -0.03418026119470596, + 0, + 0, + -0.06170174479484558, + -0.09910255670547485, + -0.07873842865228653, + -0.013664050959050655, + -0.05212222784757614, + 0, + -0.014957047067582607, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -0.02163555845618248, + -0.02163555845618248, + -0.6897953748703003, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -0.14927265048027039, + -0.010437005199491978, + 0, + 0, + 0, + 0, + 0, + 0, + -0.017030984163284302, + -0.00009560387843521312, + 0, + -0.01634802483022213, + -0.015698639675974846, + 0, + 7.572530269622803, + 7.000411510467529, + -0.5314501523971558, + -0.5314501523971558, + -0.4734956401909367 + ], + [ + 1.0063420534133911, + -0.30277952551841736, + -0.6083040237426758, + -0.6710527539253235, + -0.6855297684669495, + -0.030787145718932152, + -0.7419338226318359, + 0.5955686569213867, + -0.34317153692245483, + -0.37119558453559875, + -0.09519360959529877, + -0.10691992193460464, + -0.025333672761917114, + -0.025333672761917114, + -0.09519360959529877, + -0.025333672761917114, + -0.038938913494348526, + -0.09744702279567719, + -0.025333672761917114, + -0.10691992193460464, + -0.3539024293422699, + -0.025333672761917114, + -0.05085968226194382, + -0.05085968226194382, + 0, + 0, + 0, + 0, + 0, + -0.05311308801174164, + -0.21816998720169067, + -0.2906172275543213, + -0.2906172275543213, + -0.3700330853462219, + -0.1103484034538269, + -0.00015175093722064048, + -0.00015175093722064048, + -0.0064401342533528805, + -0.00015175093722064048, + -0.05705874040722847, + -0.08894956856966019, + -0.030272455886006355, + -0.15075212717056274, + -0.1544485092163086, + -0.04713387414813042, + -0.12149176001548767, + -0.04507482424378395, + -0.09408246725797653, + -0.09408246725797653, + -0.09745470434427261, + -0.04398646950721741, + -0.04217682406306267, + -0.0404415987432003, + -0.029789570719003677, + -0.06194312870502472, + -0.03551335260272026, + -0.03551335260272026, + -0.0011829889845103025, + -0.021876968443393707, + -0.0818881243467331, + -0.019806738942861557, + -0.019806738942861557, + -0.019806738942861557, + 0, + 0, + -1.0367865562438965, + -0.12502995133399963, + -0.05542667210102081, + -0.04631301388144493, + -0.1500464528799057, + -0.8274975419044495, + 0.1079937145113945, + 2.482558488845825, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -0.007324249017983675, + -0.0165301151573658, + -0.0165301151573658, + -0.20215901732444763, + -0.20215901732444763, + -0.038466840982437134, + -0.26354047656059265, + -0.26284584403038025, + -0.00009888518980005756, + -0.00009888518980005756, + -0.9729569554328918, + 0, + -0.9938164949417114, + -1.7601009607315063, + -0.11758595705032349, + 0, + -0.11758595705032349, + 0, + 0, + 0, + 0, + -0.07950513064861298, + -0.07950513064861298, + -0.07950513064861298, + 0, + 0, + -0.05576881393790245, + -0.05576881393790245, + -0.05576881393790245, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -0.6361966729164124, + 0, + -0.6361966729164124, + 0, + 0, + 0, + 0, + 0, + 5.785028457641602, + 5.785028457641602, + -0.4704635307696706 + ] + ] + } + } + } + } + }, + "intentDomains": {}, + "extraSentences": [ + [ + "en", + "Can I cancel my order?" + ], + [ + "en", + "Why is my order cancelled?" + ], + [ + "en", + "How do I can cancel my order" + ], + [ + "en", + "Can I cancel an order" + ], + [ + "en", + "My payment was processed successfully but I didn't get any order confirmation. What should I do?" + ], + [ + "en", + "Payment was completed but no confirmation" + ], + [ + "en", + "Order was not confirmed" + ], + [ + "en", + "Money deducted but order is not confirmed" + ], + [ + "en", + "Why is my order delayed? " + ], + [ + "en", + "Did my order get delayed?" + ], + [ + "en", + "Why is my order not delivered yet?" + ], + [ + "en", + "When do I get my delivery?" + ], + [ + "en", + "How long does delivery take?" + ], + [ + "en", + "How long does shipping take?" + ], + [ + "en", + "Please Tell me about my delivery" + ], + [ + "en", + "When do I get my delivery?" + ], + [ + "en", + "Why is my order not delivered yet" + ], + [ + "en", + "goodbye" + ], + [ + "en", + "bye take care" + ], + [ + "en", + "see you later" + ], + [ + "en", + "bye for now" + ], + [ + "en", + "i must go" + ], + [ + "en", + "hello" + ], + [ + "en", + "hi" + ], + [ + "en", + "howdy" + ], + [ + "en", + "Greetings" + ], + [ + "en", + "Is anyone there?" + ], + [ + "en", + "Hello" + ], + [ + "en", + "Good day" + ], + [ + "en", + "Which items do you have?" + ], + [ + "en", + "What kinds of items are there?" + ], + [ + "en", + "What do you sell?" + ], + [ + "en", + "What do you offer?" + ], + [ + "en", + "What can I buy?" + ], + [ + "en", + "I'm looking for..." + ], + [ + "en", + "Do you have any..." + ], + [ + "en", + "I'm interested in..." + ], + [ + "en", + "Can I see what you have in..." + ], + [ + "en", + "I want to buy a..." + ], + [ + "en", + "I'm looking for something like this..." + ], + [ + "en", + "What are your most popular items?" + ], + [ + "en", + "What are some of your best deals?" + ], + [ + "en", + "Do you have any new arrivals?" + ], + [ + "en", + "Need more help" + ], + [ + "en", + "Help me more" + ], + [ + "en", + "can I talk to an agent" + ], + [ + "en", + "can I call customer service" + ], + [ + "en", + "customer support number" + ], + [ + "en", + "how to contact customer service" + ], + [ + "en", + "customer service number" + ], + [ + "en", + "contact number for help" + ], + [ + "en", + "helpline number" + ], + [ + "en", + "How to become a seller" + ], + [ + "en", + "How to contact a seller" + ], + [ + "en", + "What is my order status" + ], + [ + "en", + "I want to know my return status" + ], + [ + "en", + "How to return status" + ], + [ + "en", + "Do you take credit cards?" + ], + [ + "en", + "Do you accept Mastercard?" + ], + [ + "en", + "Can I pay with Cash?" + ], + [ + "en", + "Are you cash only?" + ], + [ + "en", + "What are your payment methods?" + ], + [ + "en", + "How do I pay?" + ], + [ + "en", + "How are you?" + ], + [ + "en", + "How are you doing?" + ], + [ + "en", + "How is your day?" + ], + [ + "en", + "How can I change my profile information" + ], + [ + "en", + "I want to change my password" + ], + [ + "en", + "I want to change my phone number" + ], + [ + "en", + "I want to change my address" + ], + [ + "en", + "I want to Reset my password" + ], + [ + "en", + "I want to delete my account" + ], + [ + "en", + "delete my account" + ], + [ + "en", + "Common reasons for delivery delay" + ], + [ + "en", + "common reasons for delivery delay" + ], + [ + "en", + "reasons for delay" + ], + [ + "en", + "delivery delay" + ], + [ + "en", + "Can I refund an item." + ], + [ + "en", + "I want to refund an item" + ], + [ + "en", + "can I refund my order" + ], + [ + "en", + "Are refunds available" + ], + [ + "en", + "Why is the status Refunded when it's not credited?" + ], + [ + "en", + "No refund even though status is refunded" + ], + [ + "en", + "No refund when status says refunded" + ], + [ + "en", + "I did not receive my refund money" + ], + [ + "en", + "Refund money not received" + ], + [ + "en", + "Thanks" + ], + [ + "en", + "Thank you" + ], + [ + "en", + "That's helpful" + ], + [ + "en", + "Thank's a lot!" + ], + [ + "en", + "thx" + ], + [ + "en", + "thnks" + ], + [ + "en", + "How can I track my order" + ], + [ + "en", + "I want to track my order" + ], + [ + "en", + "Can I track my order" + ], + [ + "en", + "Track order" + ], + [ + "en", + "I'm good" + ], + [ + "en", + "Im good" + ], + [ + "en", + "Im doing good" + ], + [ + "en", + "I am good" + ], + [ + "en", + "I am okay" + ], + [ + "en", + "How to use a voucher?" + ], + [ + "en", + "Can I use a voucher?" + ], + [ + "en", + "How to use a voucher?" + ] + ] + }, + "ner": { + "settings": { + "tag": "ner", + "entityPreffix": "%", + "entitySuffix": "%" + }, + "rules": {} + }, + "nlgManager": { + "settings": { + "tag": "nlg-manager" + }, + "responses": { + "en": { + "cancel": [ + { + "answer": "Order can only be cancelled within 7 days of placement. Digital goods do not qualify for refund." + }, + { + "answer": "Visit my orders page to check status of specific orders." + } + ], + "confirm": [ + { + "answer": "knight sends you an Email & SMS upon confirmation of your order. If it requires manual confirmation, our team will contact you within 24 hours after order placement. Delay in confirmation SMS may occur due to network error, you may receive it with a delay." + }, + { + "answer": "It takes upto 24 hours for confirmation, please bear with us! Type Need more help for more assistance. " + } + ], + "delay": [ + { + "answer": "We are really sorry if there has been a delay in your order! If your order is within delivery period, We recommend you to please wait during this period since our Delivery Heroes make 3 attempts to deliver your order!" + }, + { + "answer": "If your order is past up to 3 days after delivery period, There may be logistic issues causing a delay in delivery. Please type 'Common reasons for delivery delay' to know more about this." + }, + { + "answer": "We appreciate if you could wait for your items as most orders are delivered successfully within this period." + }, + { + "answer": "If your order is past more than 3 days, Since there may be unexpected issues causing delivery delays, you can click on 'Need more Help' for further assistance." + } + ], + "delivery": [ + { + "answer": "Delivery takes 2-4 days. Please bear with us!" + }, + { + "answer": "Shipping takes 2-4 days. Please bear with us!" + } + ], + "greetings.bye": [ + { + "answer": "see you soon!" + }, + { + "answer": "Till next time" + }, + { + "answer": "bye bye" + }, + { + "answer": "have a great day" + }, + { + "answer": "See you later, thanks for visiting. Hope I was able to help!" + }, + { + "answer": "Have a nice day. Hope I was able to help!" + } + ], + "greetings.hello": [ + { + "answer": "Hey :-) My name is knight!" + }, + { + "answer": "Hello, thanks for visiting. My name is knight!" + }, + { + "answer": "Hi there, My name is knight!. What can I do for you?" + }, + { + "answer": "Hi there, My name is knight! How can I help?" + } + ], + "items": [ + { + "answer": "Search your preference in our flagship store's search bar to see all available products. " + } + ], + "more.contact": [ + { + "answer": "Absolutely! How can I help you today? Here are some options based on your inquiry:" + }, + { + "answer": "Sure, let me know what you need help with. Here are a few things I can assist you with:" + } + ], + "more.help": [ + { + "answer": "Contact us for further information here: Phone: +250 780 000 000. Timings are from 09:00 AM to 05:00 PM from Monday to Saturday." + }, + { + "answer": "For immediate assistance, you can contact our customer service team at +250 780 000 000. Our hours are from 09:00 AM to 05:00 PM, Monday to Saturday." + } + ], + "order": [ + { + "answer": "Please visit the My Orders page for a list of your confirmed orders." + } + ], + "payments": [ + { + "answer": "We accept VISA and Mastercard" + } + ], + "personal": [ + { + "answer": "I'm good, all's good, thanks. How about you?" + } + ], + "profile.info": [ + { + "answer": "You can easily add or change your account details by following the steps below: Step 1: Click on 'Account', Step 2: Click on 'Manage my account' from the icon, Step 3: You can change or edit your name, address, email address, mobile number, etc., Step 4: Fill in the required details, and click on Save. Note: You can also change your delivery address from the Checkout page before proceeding to pay." + } + ], + "reason": [ + { + "answer": "Reasons include Seller Sourcing Issues, Courier Issues, Cross Border shipment delay, Wrong Address or Phone Number, and Unavailability of Customer. " + } + ], + "refund.demanding": [ + { + "answer": "Refund can only be issued within 7 days of placement. Digital goods do not qualify for refund." + }, + { + "answer": "Visit my orders page to check for specific orders." + } + ], + "refund.status": [ + { + "answer": "Please be patient as refunds take upto 30 days to receive into bank. " + } + ], + "thanks": [ + { + "answer": "Happy to help!" + }, + { + "answer": "Any time!" + }, + { + "answer": "My pleasure" + } + ], + "track": [ + { + "answer": "Visit the order page, click on the specific order, select 'track my order', and check the status" + } + ], + "user.response": [ + { + "answer": "Great to hear you are doing good." + } + ], + "voucher": [ + { + "answer": "You can add a voucher by clicking on My Cart > Check Out > Enter Voucher Code > APPLY. " + } + ] + } + } + }, + "actionManager": { + "settings": { + "tag": "action-manager" + }, + "actions": {} + }, + "slotManager": {} +} \ No newline at end of file diff --git a/package.json b/package.json index 06e7e40..06430f2 100644 --- a/package.json +++ b/package.json @@ -42,13 +42,16 @@ "mailgen": "^2.0.28", "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", + "node-nlp": "^4.27.0", "nodemailer": "^6.9.13", "nodemon": "^3.1.0", "passport": "^0.7.0", "passport-google-oauth20": "^2.0.0", "pg": "^8.11.5", "reflect-metadata": "^0.2.2", + "socket.io": "^4.7.5", "source-map-support": "^0.5.21", + "stripe": "^15.8.0", "superagent": "^9.0.1", "swagger-jsdoc": "^6.2.8", "swagger-ui-express": "^5.0.0", @@ -71,13 +74,16 @@ "@types/express": "^4.17.21", "@types/express-session": "^1.18.0", "@types/jest": "^29.5.12", + "@types/joi": "^17.2.3", "@types/jsend": "^1.0.32", "@types/jsonwebtoken": "^9.0.6", + "@types/mocha": "^10.0.6", "@types/morgan": "^1.9.9", "@types/node": "^20.12.7", "@types/nodemailer": "^6.4.15", "@types/passport-google-oauth20": "^2.0.16", "@types/reflect-metadata": "^0.1.0", + "@types/socket.io": "^3.0.2", "@types/supertest": "^6.0.2", "@types/uuid": "^9.0.8", "@types/winston": "^2.4.4", diff --git a/src/@types/index.d.ts b/src/@types/index.d.ts index e69de29..8228fa6 100644 --- a/src/@types/index.d.ts +++ b/src/@types/index.d.ts @@ -0,0 +1 @@ +declare module 'node-nlp'; diff --git a/src/__test__/cart.test.ts b/src/__test__/cart.test.ts index 9f86f73..ffe143f 100644 --- a/src/__test__/cart.test.ts +++ b/src/__test__/cart.test.ts @@ -1,4 +1,3 @@ - import request from 'supertest'; import jwt from 'jsonwebtoken'; import { app, server } from '../index'; @@ -15,6 +14,7 @@ import { cleanDatabase } from './test-assets/DatabaseCleanup'; const vendor1Id = uuid(); const buyer1Id = uuid(); const buyer2Id = uuid(); +const buyer3Id = uuid(); const product1Id = uuid(); const product2Id = uuid(); const catId = uuid(); @@ -53,7 +53,7 @@ const sampleBuyer1: UserInterface = { id: buyer1Id, firstName: 'buyer1', lastName: 'user', - email: 'elijahladdiedv@gmail.com', + email: 'manger@gmail.com', password: 'password', userType: 'Buyer', gender: 'Male', @@ -66,7 +66,7 @@ const sampleBuyer2: UserInterface = { id: buyer2Id, firstName: 'buyer1', lastName: 'user', - email: 'buyer1112@example.com', + email: 'elijahladdiedv@example.com', password: 'password', userType: 'Buyer', gender: 'Male', @@ -74,6 +74,18 @@ const sampleBuyer2: UserInterface = { photoUrl: 'https://example.com/photo.jpg', role: 'BUYER', }; +const sampleBuyer3: UserInterface = { + id: buyer3Id, + firstName: 'buyer1', + lastName: 'user', + email: 'elhladdiedv@example.com', + password: 'password', + userType: 'Admin', + gender: 'Male', + phoneNumber: '121163800', + photoUrl: 'https://example.com/photo.jpg', + role: 'ADMIN', +}; const sampleCat = { id: catId, @@ -171,11 +183,12 @@ beforeAll(async () => { }); afterAll(async () => { - await cleanDatabase() + await cleanDatabase(); + server.close(); }); -describe('Cart management for guest/buyer', () => { +describe('Cart| Order management for guest/buyer', () => { describe('Creating new product', () => { it('should create new product', async () => { const response = await request(app) @@ -193,7 +206,7 @@ describe('Cart management for guest/buyer', () => { expect(response.status).toBe(201); expect(response.body.data.product).toBeDefined; - }, 60000); + }); it('return an error if the number of product images exceeds 6', async () => { const response = await request(app) @@ -376,6 +389,135 @@ describe('Cart management for guest/buyer', () => { }); }); + describe('Order management tests', () => { + let orderId: any; + let productId: any; + let feedbackId: any; + let feedback2Id: any; + describe('Create order', () => { + it('should return 400 when user ID is not provided', async () => { + const response = await request(app) + .post('/product/orders') + .send({ + address: { + country: 'Test Country', + city: 'Test City', + street: 'Test Street', + }, + }) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(201); + }); + + it('should return orders for the buyer', async () => { + const response = await request(app) + .get('/product/client/orders') + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(200); + orderId = response.body.data.orders[0]?.id; + productId = response.body.data.orders[0]?.orderItems[0]?.product?.id; + }); + + + it('should get single order', async () => { + const response = await request(app) + .get(`/product/client/orders/${orderId}`) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + + expect(response.status).toBe(200); + expect(response.body.data.order).toBeDefined(); + }); + + it('should not return data for single order, if order doesn\'t exist', async () => { + const response = await request(app) + .get(`/product/client/orders/${uuid()}`) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + + expect(response.status).toBe(404); + }); + + it('should not return data for single order, for an incorrect id syntax', async () => { + const response = await request(app) + .get(`/product/client/orders/incorrectId`) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + + expect(response.status).toBe(400); + }); + + it('should return 404 if the buyer has no orders', async () => { + const response = await request(app) + .get('/product/client/orders') + .set('Authorization', `Bearer ${getAccessToken(buyer2Id, sampleBuyer2.email)}`); + expect(response.status).toBe(404); + expect(response.body.message).toBeUndefined; + }); + + it('should return transaction history for the buyer', async () => { + const response = await request(app) + .get('/product/orders/history') + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(200); + expect(response.body.message).toBe('Transaction history retrieved successfully'); + }); + + it('should return 400 when user ID is not provided', async () => { + const response = await request(app) + .get('/product/orders/history') + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(200); + }); + }); + + describe('Update order', () => { + it('should update order status successfully', async () => { + const response = await request(app) + .put(`/product/client/orders/${orderId}`) + .send({ orderStatus: 'completed' }) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(200); + }); + }); + describe('Add feedback to the product with order', () => { + it('should create new feedback to the ordered product', async () => { + const response = await request(app) + .post(`/feedback/${productId}/new`) + .send({ orderId, comment: 'Well this product looks so fantastic' }) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(201); + feedbackId = response.body.data.id + }); + it('should create new feedback to the ordered product', async () => { + const response = await request(app) + .post(`/feedback/${productId}/new`) + .send({ orderId, comment: 'Murigalike this product looks so fantastic' }) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(201); + feedback2Id = response.body.data.id + }); + it('should updated existing feedback successfully', async () => { + const response = await request(app) + .put(`/feedback/update/${feedbackId}`,) + .send({ orderId, comment: 'Well this product looks so lovely' }) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(200); + }); + it('should remove recorded feedback', async () => { + const response = await request(app) + .delete(`/feedback/delete/${feedbackId}`) + .send({ orderId, comment: 'Well this product looks so lovely' }) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); + expect(response.status).toBe(200); + }); + it('should remove recorder feedback as admin ', async () => { + const response = await request(app) + .delete(`/feedback/admin/delete/${feedback2Id}`) + .send({ orderId, comment: 'Well this product looks so lovely' }) + .set('Authorization', `Bearer ${getAccessToken(buyer3Id, sampleBuyer3.email)}`); + expect(response.status).toBe(401); + }); + }); + }); + describe('Deleting product from cart', () => { it('should return 404 if product does not exist in cart', async () => { const response = await request(app) @@ -511,106 +653,3 @@ describe('Cart management for guest/buyer', () => { }); }); }); - -describe('Order management tests', () => { - let orderId: string | null; - describe('Create order', () => { - it('should return 400 when user ID is not provided', async () => { - const response = await request(app) - .post('/product/orders') - .send({ - address: { - country: 'Test Country', - city: 'Test City', - street: 'Test Street', - }, - }).set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); - expect(response.status).toBe(400); - }); - - it('should create a new order', async () => { - - const response = await request(app) - .post('/product/orders') - .send({ - address: { - country: 'Test Country', - city: 'Test City', - street: 'Test Street', - }, - }) - .set('Authorization', `Bearer ${getAccessToken(buyer2Id, sampleBuyer2.email)}`); - - expect(response.status).toBe(400); - expect(response.body.message).toBeUndefined; - orderId = response.body.data?.orderId; // Assuming orderId is returned in response - }); - - it('should insert a new order', async () => { - - const response = await request(app) - .post('/product/orders') - .send({ - address: { - country: 'Test Country', - city: 'Test City', - street: 'Test Street', - }, - }) - .set('Authorization', `Bearer ${getAccessToken(buyer2Id, sampleBuyer2.email)}`); - - expect(response.status).toBe(400); - expect(response.body.message).toBeUndefined; - orderId = response.body.data?.orderId; // Assuming orderId is returned in response - }); - }); - - describe('Get orders', () => { - it('should return orders for the buyer', async () => { - const response = await request(app) - .get('/product/client/orders') - .set('Authorization', `Bearer ${getAccessToken(buyer2Id, sampleBuyer2.email)}`); - expect(response.status).toBe(404); - expect(response.body.message).toBeUndefined; - - }); - - it('should return 404 if the buyer has no orders', async () => { - const response = await request(app) - .get('/product/client/orders') - .set('Authorization', `Bearer ${getAccessToken(buyer2Id, sampleBuyer2.email)}`); - expect(response.status).toBe(404); - expect(response.body.message).toBeUndefined; - }); - }); - - describe('Get transaction history', () => { - it('should return transaction history for the buyer', async () => { - - const response = await request(app) - .get('/product/orders/history') - .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); - expect(response.status).toBe(404); - expect(response.body.message).toBe('No transaction history found'); - - }); - - it('should return 400 when user ID is not provided', async () => { - const response = await request(app) - .get('/product/orders/history') - .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); - expect(response.status).toBe(404); - }); - }); - - describe('Update order', () => { - it('should update order status successfully', async () => { - - const response = await request(app) - .put(`/product/client/orders/${orderId}`) - .send({ orderStatus: 'delivered' }) - .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); - expect(response.status).toBe(500); - }); - }); -}); diff --git a/src/__test__/chatBot.test.ts b/src/__test__/chatBot.test.ts new file mode 100644 index 0000000..e588ff6 --- /dev/null +++ b/src/__test__/chatBot.test.ts @@ -0,0 +1,53 @@ +import request from 'supertest'; +import { app, server } from '../index'; +import { createConnection} from 'typeorm'; +import { cleanDatabase } from './test-assets/DatabaseCleanup'; + +beforeAll(async () => { + await createConnection(); +}); + +jest.setTimeout(20000); +afterAll(async () => { + await cleanDatabase(); + server.close(); +}); + + +describe('POST /chat', () => { + it('should respond with a successful message for a valid user query', async () => { + const userMessage = 'What kind of items do you have?'; + const response = await request(app) + .post('/chat') + .send({ message: userMessage }) + .expect(200) + .expect('Content-Type', /json/); + + expect(response.body).toHaveProperty('message'); + expect(response.status).toBe(200); + expect(response.body.status).toBe('success'); + }); + it('should respond with an error for an empty user message', async () => { + const emptyMessageReq = { body: { message: '' } }; + const response = await request(app) + .post('/chat') + .send(emptyMessageReq) + + expect(response.status).toBe(400); + expect(response.body.status).toBe('error'); + expect(response.body.message).toBe('No user message'); + + }); + it('should respond with an error for an empty user message', async () => { + const userMessage = 'dojdojdodjojoqdj'; + const response = await request(app) + .post('/chat') + .send({ message: userMessage }) + expect(response.status).toBe(200); + expect(response.body.status).toBe('success'); + expect(response.body.message).toBe('Sorry, I am not sure what you mean. Can you rephrase?'); + + + }); + +}); diff --git a/src/__test__/coupon.test.ts b/src/__test__/coupon.test.ts index b3f68b4..269e95e 100644 --- a/src/__test__/coupon.test.ts +++ b/src/__test__/coupon.test.ts @@ -21,7 +21,7 @@ const product2Id = uuid(); const couponCode = 'DISCOUNT20'; const couponCode1 = 'DISCOUNT10'; const couponCode2 = 'DISCOUNT99'; -const couponCode3 = 'DISCOUNT22' +const couponCode3 = 'DISCOUNT22'; const expiredCouponCode = 'EXPIRED'; const finishedCouponCode = 'FINISHED'; const moneyCouponCode = 'MONEY'; @@ -199,11 +199,10 @@ beforeAll(async () => { const cartItemRepository = connection?.getRepository(CartItem); await cartItemRepository?.save({ ...sampleCartItem1 }); - }); afterAll(async () => { - await cleanDatabase() + await cleanDatabase(); server.close(); }); @@ -340,84 +339,87 @@ describe('Coupon Management System', () => { }); describe('Buyer Coupon Application', () => { - describe('Checking Coupon Conditions', () =>{ + describe('Checking Coupon Conditions', () => { it('should return 400 when no coupon submitted', async () => { const response = await request(app) .post(`/coupons/apply`) .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`); - expect(response.status).toBe(400); - expect(response.body.message).toBe('Coupon Code is required'); - }) + expect(response.status).toBe(400); + expect(response.body.message).toBe('Coupon Code is required'); + }); it('should return 404 if coupon code is not found in the database', async () => { const response = await request(app) .post(`/coupons/apply`) .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`) .send({ - couponCode: "InvalidCode", + couponCode: 'InvalidCode', }); - expect(response.status).toBe(404); - expect(response.body.message).toBe('Invalid Coupon Code'); - }) + expect(response.status).toBe(404); + expect(response.body.message).toBe('Invalid Coupon Code'); + }); it('should not allow use of expired tokens', async () => { const response = await request(app) - .post(`/coupons/apply`) + .post(`/coupons/apply`) .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`) .send({ couponCode: expiredCoupon.code, }); - expect(response.status).toBe(400); - expect(response.body.message).toBe('Coupon is expired'); - }) + expect(response.status).toBe(400); + expect(response.body.message).toBe('Coupon is expired'); + }); it('should not allow use of coupon that reach maximum users', async () => { const response = await request(app) - .post(`/coupons/apply`) + .post(`/coupons/apply`) .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`) .send({ couponCode: finishedCoupon.code, }); - expect(response.status).toBe(400); - expect(response.body.message).toBe('Coupon Discount Ended'); - }) + expect(response.status).toBe(400); + expect(response.body.message).toBe('Coupon Discount Ended'); + }); it('Should not work when the product is not in cart', async () => { const response = await request(app) - .post(`/coupons/apply`) - .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`) - .send({ + .post(`/coupons/apply`) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`) + .send({ couponCode: sampleCoupon3.code, }); expect(response.status).toBe(404); - expect(response.body.message).toBe("No product in Cart with that coupon code"); - }) - }) + expect(response.body.message).toBe('No product in Cart with that coupon code'); + }); + }); - describe("Giving discount according the the product coupon", () => { + describe('Giving discount according the the product coupon', () => { it('Should give discont when discount-type is percentage', async () => { const response = await request(app) - .post(`/coupons/apply`) - .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`) - .send({ + .post(`/coupons/apply`) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`) + .send({ couponCode: sampleCoupon2.code, }); expect(response.status).toBe(200); - expect(response.body.message).toBe(`Coupon Code successfully activated discount on product: ${sampleProduct1.name}`); - }) + expect(response.body.message).toBe( + `Coupon Code successfully activated discount on product: ${sampleProduct1.name}` + ); + }); it('Should give discont when discount-type is money', async () => { const response = await request(app) - .post(`/coupons/apply`) - .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`) - .send({ + .post(`/coupons/apply`) + .set('Authorization', `Bearer ${getAccessToken(buyer1Id, sampleBuyer1.email)}`) + .send({ couponCode: moneyCoupon.code, }); expect(response.status).toBe(200); - expect(response.body.message).toBe(`Coupon Code successfully activated discount on product: ${sampleProduct1.name}`); - }) - }) - -}) + expect(response.body.message).toBe( + `Coupon Code successfully activated discount on product: ${sampleProduct1.name}` + ); + }); + }); +}); diff --git a/src/__test__/errorHandler.test.ts b/src/__test__/errorHandler.test.ts index fb1437c..cf079f0 100644 --- a/src/__test__/errorHandler.test.ts +++ b/src/__test__/errorHandler.test.ts @@ -1,47 +1,47 @@ import { Request, Response } from 'express'; -import { CustomError, errorHandler } from '../middlewares/errorHandler' +import { CustomError, errorHandler } from '../middlewares/errorHandler'; describe('CustomError', () => { - it('should create a CustomError object with statusCode and status properties', () => { - const message = 'Test error message'; - const statusCode = 404; - const customError = new CustomError(message, statusCode); - expect(customError.message).toBe(message); - expect(customError.statusCode).toBe(statusCode); - expect(customError.status).toBe('fail'); - }); + it('should create a CustomError object with statusCode and status properties', () => { + const message = 'Test error message'; + const statusCode = 404; + const customError = new CustomError(message, statusCode); + expect(customError.message).toBe(message); + expect(customError.statusCode).toBe(statusCode); + expect(customError.status).toBe('fail'); }); +}); - describe('errorHandler', () => { - it('should send correct response with status code and message', () => { - const err = new CustomError('Test error message', 404); - const req = {} as Request; - const res = { - status: jest.fn().mockReturnThis(), - json: jest.fn(), - } as unknown as Response; - const next = jest.fn(); - errorHandler(err, req, res, next); - expect(res.status).toHaveBeenCalledWith(404); - expect(res.json).toHaveBeenCalledWith({ - status: 404, - message: 'Test error message', - }); +describe('errorHandler', () => { + it('should send correct response with status code and message', () => { + const err = new CustomError('Test error message', 404); + const req = {} as Request; + const res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + } as unknown as Response; + const next = jest.fn(); + errorHandler(err, req, res, next); + expect(res.status).toHaveBeenCalledWith(404); + expect(res.json).toHaveBeenCalledWith({ + status: 404, + message: 'Test error message', }); - it('should handle errors with status code 500', () => { - const err = new CustomError('something went wrong', 500); - const req = {} as Request; - const res = { - status: jest.fn().mockReturnThis(), - json: jest.fn(), - } as unknown as Response; - const next = jest.fn(); - errorHandler(err, req, res, next); + }); + it('should handle errors with status code 500', () => { + const err = new CustomError('something went wrong', 500); + const req = {} as Request; + const res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + } as unknown as Response; + const next = jest.fn(); + errorHandler(err, req, res, next); - expect(res.status).toHaveBeenCalledWith(500); - expect(res.json).toHaveBeenCalledWith({ - status: 500, - message: 'something went wrong', - }); + expect(res.status).toHaveBeenCalledWith(500); + expect(res.json).toHaveBeenCalledWith({ + status: 500, + message: 'something went wrong', }); - }); \ No newline at end of file + }); +}); diff --git a/src/__test__/getProduct.test.ts b/src/__test__/getProduct.test.ts index 88dd415..96201dd 100644 --- a/src/__test__/getProduct.test.ts +++ b/src/__test__/getProduct.test.ts @@ -7,9 +7,11 @@ import { User, UserInterface } from '../entities/User'; import { v4 as uuid } from 'uuid'; import { Product } from '../entities/Product'; import { Category } from '../entities/Category'; +import { Cart } from '../entities/Cart'; import { cleanDatabase } from './test-assets/DatabaseCleanup'; const vendor1Id = uuid(); +const BuyerID = uuid(); const product1Id = uuid(); const Invalidproduct = '11278df2-d026-457a-9471-4749f038df68'; const catId = uuid(); @@ -37,6 +39,18 @@ const sampleVendor1: UserInterface = { photoUrl: 'https://example.com/photo.jpg', role: 'VENDOR', }; +const sampleBuyer1: UserInterface = { + id: BuyerID, + firstName: 'vendor1o', + lastName: 'user', + email: 'buyer10@example.com', + password: 'password', + userType: 'Vendor', + gender: 'Male', + phoneNumber: '000380996348', + photoUrl: 'https://example.com/photo.jpg', + role: 'BUYER', +}; const sampleCat = { id: catId, @@ -53,7 +67,7 @@ const sampleProduct1 = { vendor: sampleVendor1, categories: [sampleCat], }; - +let cardID : string; beforeAll(async () => { const connection = await dbConnection(); @@ -61,15 +75,15 @@ beforeAll(async () => { await categoryRepository?.save({ ...sampleCat }); const userRepository = connection?.getRepository(User); - await userRepository?.save({ ...sampleVendor1 }); + await userRepository?.save({ ...sampleVendor1}); + await userRepository?.save({ ...sampleBuyer1 }); const productRepository = connection?.getRepository(Product); await productRepository?.save({ ...sampleProduct1 }); }); afterAll(async () => { - await cleanDatabase() - + await cleanDatabase(); server.close(); }); @@ -122,3 +136,23 @@ describe('Get single product', () => { expect(response.body.message).toBe('Product not found'); }, 10000); }); +describe('Cart Order and payment functionalities', () => { + it('should create a cart for a product', async () => { + const productId = product1Id; + const quantity = 8; + + const token = getAccessToken(BuyerID, sampleBuyer1.email); + + const response = await request(app) + .post('/cart') + .set('Authorization', `Bearer ${token}`) + .send({ productId, quantity }); + + + expect(response.status).toBe(201); + expect(response.body.data.cart).toBeDefined(); + cardID = JSON.stringify(response.body.data.cart.id) + }); + +} +) \ No newline at end of file diff --git a/src/__test__/isAllowed.test.ts b/src/__test__/isAllowed.test.ts index b17b657..471a950 100644 --- a/src/__test__/isAllowed.test.ts +++ b/src/__test__/isAllowed.test.ts @@ -48,24 +48,23 @@ beforeAll(async () => { }); afterAll(async () => { - await cleanDatabase() - + await cleanDatabase(); }); describe('Middleware - checkUserStatus', () => { - beforeEach(() => { - reqMock = {}; - resMock = { - status: jest.fn().mockReturnThis(), - json: jest.fn() - }; - nextMock = jest.fn(); - }); - - it('should return 401 if user is not authenticated', async () => { - await checkUserStatus(reqMock as Request, resMock as Response, nextMock); - expect(responseError).toHaveBeenCalledWith(resMock, 401, 'Authentication required'); - }); + beforeEach(() => { + reqMock = {}; + resMock = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + nextMock = jest.fn(); + }); + + it('should return 401 if user is not authenticated', async () => { + await checkUserStatus(reqMock as Request, resMock as Response, nextMock); + expect(responseError).toHaveBeenCalledWith(resMock, 401, 'Authentication required'); + }); it('should return 401 if user is not found', async () => { reqMock = { user: { id: uuid() } }; diff --git a/src/__test__/logout.test.ts b/src/__test__/logout.test.ts index cd950fd..ac9eefa 100644 --- a/src/__test__/logout.test.ts +++ b/src/__test__/logout.test.ts @@ -10,8 +10,7 @@ beforeAll(async () => { }); afterAll(async () => { - await cleanDatabase() - + await cleanDatabase(); server.close(); }); diff --git a/src/__test__/notification.test.ts b/src/__test__/notification.test.ts new file mode 100644 index 0000000..8c4b2e6 --- /dev/null +++ b/src/__test__/notification.test.ts @@ -0,0 +1,231 @@ +import request from 'supertest'; +import jwt from 'jsonwebtoken'; +import { app, server } from '../index'; +import { dbConnection } from '../startups/dbConnection'; +import { User, UserInterface } from '../entities/User'; +import { v4 as uuid } from 'uuid'; +import { cleanDatabase } from './test-assets/DatabaseCleanup'; +import { Notification } from '../entities/Notification'; +import { NotificationItem } from '../entities/NotificationItem'; +import exp from 'constants'; + +const user1Id = uuid(); +const user2Id = uuid(); +const user3Id = uuid(); + +const notificationId = uuid(); +const notification2Id = uuid(); + +const notificationItemId = uuid(); +const notificationItem2Id = uuid(); +const notificationItem3Id = uuid(); + +const jwtSecretKey = process.env.JWT_SECRET || ''; + +const getAccessToken = (id: string, email: string) => { + return jwt.sign( + { + id: id, + email: email, + }, + jwtSecretKey + ); +}; + +const sampleUser: UserInterface = { + id: user1Id, + firstName: 'vendor1', + lastName: 'user', + email: 'vendor1@example.com', + password: 'password', + userType: 'Vendor', + gender: 'Male', + phoneNumber: '126380996347', + photoUrl: 'https://example.com/photo.jpg', + role: 'VENDOR', +}; +const sampleUser2: UserInterface = { + id: user2Id, + firstName: 'buyer1', + lastName: 'user', + email: 'buyer1@example.com', + password: 'password', + userType: 'Buyer', + gender: 'Male', + phoneNumber: '126380996347', + photoUrl: 'https://example.com/photo.jpg', + role: 'BUYER', +}; + +const sampleUser3: UserInterface = { + id: user3Id, + firstName: 'buyer2', + lastName: 'user', + email: 'buyer2@example.com', + password: 'password', + userType: 'Buyer', + gender: 'Male', + phoneNumber: '1347', + photoUrl: 'https://example.com/photo.jpg', + role: 'BUYER', +}; + +const sampleNotification = { + id: notificationId, + unRead: 2, + user: sampleUser +}; + +const sampleNotification2 = { + id: notification2Id, + unRead: 1, + user: sampleUser3 +}; + +const sampleNotificationItem = { + id: notificationItemId, + content: "This is notification content for test", + type: 'order', + isRead: false, + link: '/link/to/more-detail', + notification: sampleNotification +}; + + +const sampleNotificationItem2 = { + id: notificationItem2Id, + content: "This is notification content for test", + type: 'order', + isRead: false, + link: '/link/to/more-detail', + notification: sampleNotification +}; + +const sampleNotificationItem3 = new NotificationItem(); +sampleNotificationItem3.id = notificationItem3Id; +sampleNotificationItem3.content = "This is notification content for test"; +sampleNotificationItem3.type = 'order'; +sampleNotificationItem3.isRead = false; +sampleNotificationItem3.link = '/link/to/more-details'; + + +beforeAll(async () => { + const connection = await dbConnection(); + + const userRepository = connection?.getRepository(User); + await userRepository?.save({ ...sampleUser }); + await userRepository?.save({ ...sampleUser2 }); + await userRepository?.save({ ...sampleUser3 }); + + const notificationRepository = connection?.getRepository(Notification); + await notificationRepository?.save({ ...sampleNotification }); + + const result = await notificationRepository?.save({ ...sampleNotification2 }); + + const notificationItemRepository = connection?.getRepository(NotificationItem); + if (result) sampleNotificationItem3.notification = result; + await notificationItemRepository?.save({ ...sampleNotificationItem }); + await notificationItemRepository?.save({ ...sampleNotificationItem2 }); + await notificationItemRepository?.save({ ...sampleNotificationItem3 }); +}); + +afterAll(async () => { + await cleanDatabase(); + + server.close(); +}); + +describe('Notifications Tests', () => { + it('Should return all notification for authenticated user', async () => { + const response = await request(app) + .get('/notification/') + .set('Authorization', `Bearer ${getAccessToken(sampleUser.id!, sampleUser.email)}`); + + expect(response.status).toBe(200); + expect(response.body.data.message).toBe('Notifications retrieved successfully'); + }); + + it('Should return empty object if user doesn\'t have any notification', async () => { + const response = await request(app) + .get('/notification/') + .set('Authorization', `Bearer ${getAccessToken(sampleUser2.id!, sampleUser2.email)}`); + + expect(response.status).toBe(200); + expect(response.body.data.message).toBe('User doesn\'t have any notifications.'); + }); + + it('should update selected notifications, if there valid Ids and exist in DB', async () => { + const response = await request(app) + .put(`/notification/`) + .send({ + notificationIds: [notificationItemId, 'sdfsdfd'] + }) + .set('Authorization', `Bearer ${getAccessToken(sampleUser.id!, sampleUser.email)}`); + + expect(response.status).toBe(200); + expect(response.body.data.message).toBe('1 of 2 Notification(s) was successfully updated.'); + }); + + it('should return 400, if no notifications ids provided to update', async () => { + const response = await request(app) + .put(`/notification/`) + .set('Authorization', `Bearer ${getAccessToken(sampleUser.id!, sampleUser.email)}`); + + expect(response.status).toBe(400); + }); + + it('should update all unread notifications for authenticated user', async () => { + const response = await request(app) + .put(`/notification/all`) + .set('Authorization', `Bearer ${getAccessToken(sampleUser.id!, sampleUser.email)}`); + + expect(response.status).toBe(200); + expect(response.body.data.message).toBe('All your unread notifications was successfully updated as read.'); + }); + + it('should not update any notification if user doesn\'t have unread notifications.', async () => { + const response = await request(app) + .put(`/notification/all`) + .set('Authorization', `Bearer ${getAccessToken(sampleUser2.id!, sampleUser2.email)}`); + + expect(response.status).toBe(200); + expect(response.body.data.message).toBe('User doesn\'t have any unread notifications.'); + }); + + it('should delete selected notifications, if there valid Ids and exist in DB', async () => { + const response = await request(app) + .delete(`/notification/`) + .send({ + notificationIds: [notificationItem3Id, 'sdfsdfd'] + }) + .set('Authorization', `Bearer ${getAccessToken(sampleUser3.id!, sampleUser3.email)}`); + + expect(response.status).toBe(200); + expect(response.body.data.message).toBe('1 of 2 Notification(s) was successfully deleted.'); + }); + + it('should return 400, if no notifications ids provided to delete', async () => { + const response = await request(app) + .delete(`/notification/`) + .set('Authorization', `Bearer ${getAccessToken(sampleUser.id!, sampleUser.email)}`); + + expect(response.status).toBe(400); + }); + + it('should delete all notification for authenticated user.', async () => { + const response = await request(app) + .delete(`/notification/all`) + .set('Authorization', `Bearer ${getAccessToken(sampleUser.id!, sampleUser.email)}`); + + expect(response.status).toBe(200); + expect(response.body.data.message).toBe('All Notifications was successfully deleted.'); + }); + it('Should not delete any notification, if user doesn\'t have notifications', async () => { + const response = await request(app) + .delete('/notification/all') + .set('Authorization', `Bearer ${getAccessToken(sampleUser2.id!, sampleUser2.email)}`); + + expect(response.status).toBe(404); + expect(response.body.message).toBe('User doesn\'t have notifications'); + }); +}); \ No newline at end of file diff --git a/src/__test__/oauth.test.ts b/src/__test__/oauth.test.ts index 2493059..877d63b 100644 --- a/src/__test__/oauth.test.ts +++ b/src/__test__/oauth.test.ts @@ -5,24 +5,20 @@ import { User } from '../entities/User'; import { cleanDatabase } from './test-assets/DatabaseCleanup'; beforeAll(async () => { - await createConnection(); }); afterAll(async () => { - await cleanDatabase() + await cleanDatabase(); server.close(); }); -describe('authentication routes test',() => { - it('should redirect to the google authentication page',async() => { - const response = await request(app) - .get('/user/google-auth'); - expect(response.statusCode).toBe(302) - }) - it('should redirect after google authentication', async() => { - const response = await request(app) - .get('/user/auth/google/callback'); - expect(response.statusCode).toBe(302) - }) +describe('authentication routes test', () => { + it('should redirect to the google authentication page', async () => { + const response = await request(app).get('/user/google-auth'); + expect(response.statusCode).toBe(302); + }); + it('should redirect after google authentication', async () => { + const response = await request(app).get('/user/auth/google/callback'); + expect(response.statusCode).toBe(302); + }); }); - diff --git a/src/__test__/orderManagement.test.ts b/src/__test__/orderManagement.test.ts new file mode 100644 index 0000000..846b9d8 --- /dev/null +++ b/src/__test__/orderManagement.test.ts @@ -0,0 +1,390 @@ +import request from 'supertest'; +import jwt from 'jsonwebtoken'; +import { app, server } from '../index'; +import { getConnection } from 'typeorm'; +import { dbConnection } from '../startups/dbConnection'; +import { User, UserInterface } from '../entities/User'; +import { v4 as uuid } from 'uuid'; +import { Product } from '../entities/Product'; +import { Category } from '../entities/Category'; +import { cleanDatabase } from './test-assets/DatabaseCleanup'; +import { Order } from '../entities/Order'; +import { OrderItem } from '../entities/OrderItem'; +import { VendorOrders } from '../entities/vendorOrders'; +import { VendorOrderItem } from '../entities/VendorOrderItem'; + +const adminId = uuid(); +const vendorId = uuid(); +const vendor2Id = uuid(); +const buyerId = uuid(); + +const productId = uuid(); +const product2Id = uuid(); + +const orderId = uuid(); +const orderItemId = uuid(); +const order2Id = uuid(); +const order2ItemId = uuid(); + +const vendorOrderId = uuid(); +const vendorOrderItemId = uuid(); +const vendorOrder2Id = uuid(); +const vendorOrder2ItemId = uuid(); +const catId = uuid(); + +console.log(adminId, vendorId, buyerId); + +const jwtSecretKey = process.env.JWT_SECRET || ''; + +const getAccessToken = (id: string, email: string) => { + return jwt.sign( + { + id: id, + email: email, + }, + jwtSecretKey + ); +}; + +const sampleAdmin: UserInterface = { + id: adminId, + firstName: 'admin', + lastName: 'user', + email: 'admin@example.com', + password: 'password', + userType: 'Admin', + gender: 'Male', + phoneNumber: '126380997', + photoUrl: 'https://example.com/photo.jpg', + verified: true, + role: 'ADMIN', +}; +const sampleVendor: UserInterface = { + id: vendorId, + firstName: 'vendor', + lastName: 'user', + email: 'vendor@example.com', + password: 'password', + userType: 'Vendor', + gender: 'Male', + phoneNumber: '126380996347', + photoUrl: 'https://example.com/photo.jpg', + verified: true, + role: 'VENDOR', +}; +const sampleVendor2: UserInterface = { + id: vendor2Id, + firstName: 'vendor', + lastName: 'user', + email: 'vendor2@example.com', + password: 'password', + userType: 'Vendor', + gender: 'Male', + phoneNumber: '18090296347', + photoUrl: 'https://example.com/photo.jpg', + verified: true, + role: 'VENDOR', +}; +const sampleBuyer: UserInterface = { + id: buyerId, + firstName: 'buyer', + lastName: 'user', + email: 'buyer@example.com', + password: 'password', + userType: 'Buyer', + gender: 'Male', + phoneNumber: '6380996347', + photoUrl: 'https://example.com/photo.jpg', + verified: true, + role: 'BUYER', +}; + +const sampleCat = { + id: catId, + name: 'accessories', +}; + +const sampleProduct = { + id: productId, + name: 'test product', + description: 'amazing product', + images: ['photo1.jpg', 'photo2.jpg', 'photo3.jpg'], + newPrice: 200, + quantity: 10, + vendor: sampleVendor, + categories: [sampleCat], +}; +const sampleProduct2 = { + id: product2Id, + name: 'test product2', + description: 'amazing products', + images: ['photo1.jpg', 'photo2.jpg', 'photo3.jpg'], + newPrice: 200, + quantity: 10, + vendor: sampleVendor, + categories: [sampleCat], +}; + +const sampleOrder = { + id: orderId, + totalPrice: 400, + quantity: 2, + orderDate: new Date(), + buyer: sampleBuyer, + orderStatus: 'received', + address: 'Rwanda, Kigali, KK20st', +}; +const sampleOrder2 = { + id: order2Id, + totalPrice: 400, + quantity: 2, + orderDate: new Date(), + buyer: sampleBuyer, + orderStatus: 'order placed', + address: 'Rwanda, Kigali, KK20st', +}; + +const sampleOrderItem = { + id: orderItemId, + price: 200, + quantity: 2, + order: sampleOrder, + product: sampleProduct, +}; + +const sampleVendorOrder = { + id: vendorOrderId, + totalPrice: 400, + quantity: 2, + vendor: sampleVendor, + order: sampleOrder, + buyer: sampleBuyer, + orderStatus: 'pending', +}; + +const sampleVendorOrderItem = { + 'id': vendorOrderItemId, + 'price/unit': 200, + 'quantity': 2, + 'order': sampleVendorOrder, + 'product': sampleProduct, +}; + +beforeAll(async () => { + const connection = await dbConnection(); + + const categoryRepository = connection?.getRepository(Category); + await categoryRepository?.save({ ...sampleCat }); + + const userRepository = connection?.getRepository(User); + await userRepository?.save([sampleAdmin, sampleVendor, sampleVendor2, sampleBuyer]); + + const productRepository = connection?.getRepository(Product); + await productRepository?.save({ ...sampleProduct }); + + // Order Management + const orderRepository = connection?.getRepository(Order); + await orderRepository?.save([sampleOrder, sampleOrder2]); + + const orderItemRepository = connection?.getRepository(OrderItem); + await orderItemRepository?.save({ ...sampleOrderItem }); + + const vendorOrderRepository = connection?.getRepository(VendorOrders); + await vendorOrderRepository?.save({ ...sampleVendorOrder }); + + const vendorOrderItemRepository = connection?.getRepository(VendorOrderItem); + await vendorOrderItemRepository?.save({ ...sampleVendorOrderItem }); +}); + +afterAll(async () => { + await cleanDatabase(); + server.close(); +}); + +describe('Vendor Order Management', () => { + describe('Fetching vendor Order(s)', () => { + it('Should return all vendor orders', async () => { + const response = await request(app) + .get('/product/vendor/orders') + .set('Authorization', `Bearer ${getAccessToken(vendorId, sampleVendor.email)}`); + + expect(response.status).toBe(200); + expect(response.body.data.orders).toBeDefined(); + }); + + it("Should return empty array if vendor don't have any order for buyer", async () => { + const response = await request(app) + .get('/product/vendor/orders') + .set('Authorization', `Bearer ${getAccessToken(vendor2Id, sampleVendor2.email)}`); + + expect(response.status).toBe(200); + expect(response.body.data.orders).toEqual([]); + }); + + it('Should return single vendor order', async () => { + const response = await request(app) + .get(`/product/vendor/orders/${vendorOrderId}`) + .set('Authorization', `Bearer ${getAccessToken(vendorId, sampleVendor.email)}`); + + expect(response.status).toBe(200); + expect(response.body.data.order).toBeDefined(); + }); + + it('return 404, for non existing vendor order', async () => { + const response = await request(app) + .get(`/product/vendor/orders/${uuid()}`) + .set('Authorization', `Bearer ${getAccessToken(vendorId, sampleVendor.email)}`); + + expect(response.status).toBe(404); + expect(response.body.message).toBe('Order Not Found.'); + }); + + it('return 400, for invalid vendor order id ', async () => { + const response = await request(app) + .get(`/product/vendor/orders/32df3`) + .set('Authorization', `Bearer ${getAccessToken(vendorId, sampleVendor.email)}`); + + expect(response.status).toBe(400); + expect(response.body.message).toBe(`invalid input syntax for type uuid: "32df3"`); + }); + }); + describe('Updating vendor order', () => { + it('should update the order', async () => { + const response = await request(app) + .put(`/product/vendor/orders/${vendorOrderId}`) + .set('Authorization', `Bearer ${getAccessToken(vendorId, sampleVendor.email)}`) + .send({ + orderStatus: 'delivered', + }); + + expect(response.statusCode).toBe(200); + }); + it('should not update if orderStatus in not among defined ones', async () => { + const response = await request(app) + .put(`/product/vendor/orders/${vendorOrderId}`) + .set('Authorization', `Bearer ${getAccessToken(vendorId, sampleVendor.email)}`) + .send({ + orderStatus: 'fakeOrderStatus', + }); + + expect(response.statusCode).toBe(400); + expect(response.body.message).toBe('Please provide one of defined statuses.'); + }); + it('should not update, return 404 for non existing vendor order', async () => { + const response = await request(app) + .put(`/product/vendor/orders/${uuid()}`) + .set('Authorization', `Bearer ${getAccessToken(vendorId, sampleVendor.email)}`) + .send({ + orderStatus: 'is-accepted', + }); + + expect(response.statusCode).toBe(404); + expect(response.body.message).toBe('Order Not Found.'); + }); + it('should not update, if the order has already been cancelled or completed', async () => { + const response = await request(app) + .put(`/product/vendor/orders/${vendorOrderId}`) + .set('Authorization', `Bearer ${getAccessToken(vendorId, sampleVendor.email)}`) + .send({ + orderStatus: 'is-accepted', + }); + + expect(response.statusCode).toBe(409); + }); + it('return 400, for invalid vendor order id ', async () => { + const response = await request(app) + .put(`/product/vendor/orders/32df3`) + .set('Authorization', `Bearer ${getAccessToken(vendorId, sampleVendor.email)}`) + .send({ + orderStatus: 'is-accepted', + }); + + expect(response.status).toBe(400); + expect(response.body.message).toBe(`invalid input syntax for type uuid: "32df3"`); + }); + }); +}); + +describe('Admin Order Management', () => { + describe('Fetching buyer and vendor Order(s)', () => { + it("Should return all orders with it's buyer and related vendors", async () => { + const response = await request(app) + .get('/product/admin/orders') + .set('Authorization', `Bearer ${getAccessToken(adminId, sampleAdmin.email)}`); + + expect(response.status).toBe(200); + expect(response.body.data.orders).toBeDefined(); + }); + + it('Should return single order details', async () => { + const response = await request(app) + .get(`/product/admin/orders/${orderId}`) + .set('Authorization', `Bearer ${getAccessToken(adminId, sampleAdmin.email)}`); + + expect(response.status).toBe(200); + expect(response.body.data.order).toBeDefined(); + }); + + it('return 404, for non existing order', async () => { + const response = await request(app) + .get(`/product/admin/orders/${uuid()}`) + .set('Authorization', `Bearer ${getAccessToken(adminId, sampleAdmin.email)}`); + + expect(response.status).toBe(404); + expect(response.body.message).toBe('Order Not Found.'); + }); + + it('return 400, for invalid order id ', async () => { + const response = await request(app) + .get(`/product/admin/orders/32df3`) + .set('Authorization', `Bearer ${getAccessToken(adminId, sampleAdmin.email)}`); + + expect(response.status).toBe(400); + expect(response.body.message).toBe(`invalid input syntax for type uuid: "32df3"`); + }); + }); + describe('Updating buyer and vendor order', () => { + it('should not update, return 404 for non existing order', async () => { + const response = await request(app) + .put(`/product/admin/orders/${uuid()}`) + .set('Authorization', `Bearer ${getAccessToken(adminId, sampleAdmin.email)}`); + + expect(response.statusCode).toBe(404); + expect(response.body.message).toBe('Order Not Found.'); + }); + it('should update the order', async () => { + const response = await request(app) + .put(`/product/admin/orders/${orderId}`) + .set('Authorization', `Bearer ${getAccessToken(adminId, sampleAdmin.email)}`); + + expect(response.statusCode).toBe(200); + expect(response.body.data.order).toBeDefined(); + }); + it('should not update if it has already been completed(closed)', async () => { + const response = await request(app) + .put(`/product/admin/orders/${orderId}`) + .set('Authorization', `Bearer ${getAccessToken(adminId, sampleAdmin.email)}`); + + expect(response.statusCode).toBe(409); + expect(response.body.message).toBe('The order has already been completed.'); + }); + + it('should not update, if the order has not been marked as received by buyer', async () => { + const response = await request(app) + .put(`/product/admin/orders/${order2Id}`) + .set('Authorization', `Bearer ${getAccessToken(adminId, sampleAdmin.email)}`); + + expect(response.statusCode).toBe(409); + expect(response.body.message).toBe('Order closure failed: The buyer has not received the item yet.'); + }); + + it('return 400, for invalid order id ', async () => { + const response = await request(app) + .put(`/product/admin/orders/32df3`) + .set('Authorization', `Bearer ${getAccessToken(adminId, sampleAdmin.email)}`); + + expect(response.status).toBe(400); + expect(response.body.message).toBe(`invalid input syntax for type uuid: "32df3"`); + }); + }); +}); diff --git a/src/__test__/productStatus.test.ts b/src/__test__/productStatus.test.ts index 6d6df6a..8e8b42a 100644 --- a/src/__test__/productStatus.test.ts +++ b/src/__test__/productStatus.test.ts @@ -144,7 +144,7 @@ beforeAll(async () => { }); afterAll(async () => { - await cleanDatabase() + await cleanDatabase(); server.close(); }); @@ -222,22 +222,16 @@ describe('Vendor product availability status management tests', () => { }); }); - describe('search product by name availability tests', () => { it('Should search product by name', async () => { - const response = await request(app) - .get(`/product/search?name=testingmkknkkjiproduct4`) + const response = await request(app).get(`/product/search?name=testingmkknkkjiproduct4`); expect(response.body.data).toBeDefined; }, 10000); it('should return empty array if there is product is not found in the database', async () => { - const response = await request(app) - .put(`/product/search?name=home`) - + const response = await request(app).put(`/product/search?name=home`); expect(response.statusCode).toBe(401); expect(response.body.data).toBeUndefined; }); - - }); - +}); diff --git a/src/__test__/roleCheck.test.ts b/src/__test__/roleCheck.test.ts index ada2271..32df044 100644 --- a/src/__test__/roleCheck.test.ts +++ b/src/__test__/roleCheck.test.ts @@ -35,7 +35,7 @@ beforeAll(async () => { }); afterAll(async () => { - await cleanDatabase() + await cleanDatabase(); }); describe('hasRole MiddleWare Test', () => { diff --git a/src/__test__/route.test.ts b/src/__test__/route.test.ts index 721f763..ac704b5 100644 --- a/src/__test__/route.test.ts +++ b/src/__test__/route.test.ts @@ -11,8 +11,7 @@ beforeAll(async () => { jest.setTimeout(20000); afterAll(async () => { - await cleanDatabase() - + await cleanDatabase(); server.close(); }); diff --git a/src/__test__/test-assets/DatabaseCleanup.ts b/src/__test__/test-assets/DatabaseCleanup.ts index ec40ee6..b5fbb58 100644 --- a/src/__test__/test-assets/DatabaseCleanup.ts +++ b/src/__test__/test-assets/DatabaseCleanup.ts @@ -1,28 +1,37 @@ - import { Transaction } from '../../entities/transaction'; -import { Cart } from "../../entities/Cart"; -import { CartItem } from "../../entities/CartItem"; -import { Order } from "../../entities/Order"; -import { OrderItem } from "../../entities/OrderItem"; -import { wishList } from "../../entities/wishList"; +import { Cart } from '../../entities/Cart'; +import { CartItem } from '../../entities/CartItem'; +import { Order } from '../../entities/Order'; +import { OrderItem } from '../../entities/OrderItem'; +import { wishList } from '../../entities/wishList'; import { getConnection } from 'typeorm'; import { Product } from '../../entities/Product'; import { Category } from '../../entities/Category'; import { Coupon } from '../../entities/coupon'; import { User } from '../../entities/User'; import { server } from '../..'; +import { VendorOrderItem } from '../../entities/VendorOrderItem'; +import { VendorOrders } from '../../entities/vendorOrders'; +import { Feedback } from '../../entities/Feedback'; +import { NotificationItem } from '../../entities/NotificationItem'; +import { Notification } from '../../entities/Notification'; export const cleanDatabase = async () => { const connection = getConnection(); // Delete from child tables first + await connection.getRepository(Feedback).delete({}); await connection.getRepository(Transaction).delete({}); await connection.getRepository(Coupon).delete({}); + await connection.getRepository(VendorOrderItem).delete({}); + await connection.getRepository(VendorOrders).delete({}); await connection.getRepository(OrderItem).delete({}); await connection.getRepository(Order).delete({}); await connection.getRepository(CartItem).delete({}); await connection.getRepository(Cart).delete({}); await connection.getRepository(wishList).delete({}); + await connection.getRepository(NotificationItem).delete({}); + await connection.getRepository(Notification).delete({}); // Many-to-Many relations // Clear junction table entries before deleting products and categories @@ -37,12 +46,11 @@ export const cleanDatabase = async () => { await connection.getRepository(User).delete({}); await connection.close(); - server.close(); }; -// Execute the clean-up function -cleanDatabase().then(() => { - console.log('Database cleaned'); -}).catch(error => { - console.error('Error cleaning database:', error); -}); +// // Execute the clean-up function +// cleanDatabase().then(() => { +// console.log('Database cleaned'); +// }).catch(error => { +// console.error('Error cleaning database:', error); +// }); diff --git a/src/__test__/userServices.test.ts b/src/__test__/userServices.test.ts index b4e87f9..29a2e7c 100644 --- a/src/__test__/userServices.test.ts +++ b/src/__test__/userServices.test.ts @@ -1,6 +1,6 @@ import request from 'supertest'; import { app, server } from '../index'; -import { createConnection, getConnection, getConnectionOptions, getRepository } from 'typeorm'; +import { createConnection, getRepository } from 'typeorm'; import { User } from '../entities/User'; import { cleanDatabase } from './test-assets/DatabaseCleanup'; @@ -9,7 +9,7 @@ beforeAll(async () => { }); afterAll(async () => { - await cleanDatabase() + await cleanDatabase(); server.close(); }); @@ -227,4 +227,4 @@ describe('start2FAProcess', () => { expect(res.status).toBe(404); expect(res.body).toEqual({ status: 'error', message: 'Incorrect email or password' }); }, 10000); -}); \ No newline at end of file +}); diff --git a/src/__test__/userStatus.test.ts b/src/__test__/userStatus.test.ts index 132134f..69e892a 100644 --- a/src/__test__/userStatus.test.ts +++ b/src/__test__/userStatus.test.ts @@ -36,7 +36,7 @@ beforeAll(async () => { }); afterAll(async () => { - await cleanDatabase() + await cleanDatabase(); server.close(); }); diff --git a/src/__test__/vendorProduct.test.ts b/src/__test__/vendorProduct.test.ts index f90d80d..d8fc0a5 100644 --- a/src/__test__/vendorProduct.test.ts +++ b/src/__test__/vendorProduct.test.ts @@ -110,7 +110,7 @@ beforeAll(async () => { }); afterAll(async () => { - await cleanDatabase() + await cleanDatabase(); server.close(); }); diff --git a/src/__test__/wishList.test.ts b/src/__test__/wishList.test.ts index aac072d..6658853 100644 --- a/src/__test__/wishList.test.ts +++ b/src/__test__/wishList.test.ts @@ -65,7 +65,7 @@ beforeAll(async () => { }); afterAll(async () => { - await cleanDatabase() + await cleanDatabase(); server.close(); }); const data1 = { diff --git a/src/controllers/adminOrdercontroller.ts b/src/controllers/adminOrdercontroller.ts new file mode 100644 index 0000000..388220d --- /dev/null +++ b/src/controllers/adminOrdercontroller.ts @@ -0,0 +1,18 @@ +import { Request, Response } from 'express'; +import { + getSingleBuyerVendorOrderService, + getBuyerVendorOrdersService, + updateBuyerVendorOrderService, +} from '../services'; + +export const getBuyerVendorOrders = async (req: Request, res: Response) => { + await getBuyerVendorOrdersService(req, res); +}; + +export const getSingleBuyerVendorOrder = async (req: Request, res: Response) => { + await getSingleBuyerVendorOrderService(req, res); +}; + +export const updateBuyerVendorOrder = async (req: Request, res: Response) => { + await updateBuyerVendorOrderService(req, res); +}; diff --git a/src/controllers/chatBotController.ts b/src/controllers/chatBotController.ts new file mode 100644 index 0000000..5c3d366 --- /dev/null +++ b/src/controllers/chatBotController.ts @@ -0,0 +1,6 @@ +import { Request, Response } from 'express'; +import { chatBot } from '../services'; + +export const chatBotController = async (req: Request, res: Response) => { + await chatBot(req, res); +}; diff --git a/src/controllers/couponController.ts b/src/controllers/couponController.ts index dd7e19f..e5a6804 100644 --- a/src/controllers/couponController.ts +++ b/src/controllers/couponController.ts @@ -4,7 +4,7 @@ import { updateCouponService } from '../services/couponServices/updateService'; import { deleteCouponService } from '../services/couponServices/deleteCoupon'; import { accessAllCouponService } from '../services/couponServices/accessAllCoupon'; import { readCouponService } from '../services/couponServices/readCoupon'; -import { buyerApplyCouponService } from '../services/couponServices/buyerApplyCoupon' +import { buyerApplyCouponService } from '../services/couponServices/buyerApplyCoupon'; export const createCoupon = async (req: Request, res: Response) => { await createCouponService(req, res); @@ -28,4 +28,4 @@ export const readCoupon = async (req: Request, res: Response) => { export const buyerApplyCoupon = async (req: Request, res: Response) => { await buyerApplyCouponService(req, res); -}; \ No newline at end of file +}; diff --git a/src/controllers/feedbackController.ts b/src/controllers/feedbackController.ts new file mode 100644 index 0000000..0cbce14 --- /dev/null +++ b/src/controllers/feedbackController.ts @@ -0,0 +1,21 @@ +import { Request, Response } from 'express'; +import { createFeedbackService } from '../services/feedbackServices/createFeedback'; +import { updateFeedbackService } from '../services/feedbackServices/updateFeedback'; +import { deleteFeedbackService } from '../services/feedbackServices/deleteFeedback'; +import { adminDeleteFeedbackService } from '../services/feedbackServices/adminDeleteFeedback'; + +export const createFeedback = async (req: Request, res: Response) => { + await createFeedbackService(req, res); +}; + +export const updateFeedback = async (req: Request, res: Response) => { + await updateFeedbackService(req, res); +}; + +export const deleteFeedback = async (req: Request, res: Response) => { + await deleteFeedbackService(req, res); +}; + +export const adminDeleteFeedback = async (req: Request, res: Response) => { + await adminDeleteFeedbackService(req, res); +}; diff --git a/src/controllers/index.ts b/src/controllers/index.ts index 3cbb7dc..adaf1d1 100644 --- a/src/controllers/index.ts +++ b/src/controllers/index.ts @@ -1,3 +1,6 @@ export * from './authController'; export * from './productController'; -export * from './orderController'; \ No newline at end of file +export * from './orderController'; +export * from './vendorOrderController'; +export * from './adminOrdercontroller'; +export * from './chatBotController'; \ No newline at end of file diff --git a/src/controllers/notificationControllers.ts b/src/controllers/notificationControllers.ts new file mode 100644 index 0000000..1255efd --- /dev/null +++ b/src/controllers/notificationControllers.ts @@ -0,0 +1,22 @@ +import { Request, Response } from 'express'; +import { getNotificationsService, updateNotificationsService, deleteSelectedNotificationService, deleteAllNotificationService, updateAllNotificationsService } from '../services'; + +export const getAllNotifications = async (req: Request, res: Response) => { + await getNotificationsService(req, res); +}; + +export const deleteSelectedNotifications = async (req: Request, res: Response) => { + await deleteSelectedNotificationService(req, res); +}; + +export const deleteAllNotifications = async (req: Request, res: Response) => { + await deleteAllNotificationService(req, res); +}; + +export const updateNotifications = async (req: Request, res: Response) => { + await updateNotificationsService(req, res); +}; + +export const updateAllNotifications = async (req: Request, res: Response) => { + await updateAllNotificationsService(req, res); +}; \ No newline at end of file diff --git a/src/controllers/orderController.ts b/src/controllers/orderController.ts index 5a5db97..7a877bb 100644 --- a/src/controllers/orderController.ts +++ b/src/controllers/orderController.ts @@ -1,6 +1,6 @@ import { Request, Response } from 'express'; import { createOrderService } from '../services/orderServices/createOrder'; -import { getOrdersService } from '../services/orderServices/getOrderService'; +import { getOrderService, getOrdersService } from '../services/orderServices/getOrderService'; import { updateOrderService } from '../services/orderServices/updateOrderService'; import { getTransactionHistoryService } from '../services/orderServices/getOrderTransactionHistory'; @@ -10,6 +10,10 @@ export const createOrder = async (req: Request, res: Response) => { export const getOrders = async (req: Request, res: Response) => { await getOrdersService(req, res); }; + +export const getOrder = async (req: Request, res: Response) => { + await getOrderService(req, res); +}; export const updateOrder = async (req: Request, res: Response) => { await updateOrderService(req, res); }; diff --git a/src/controllers/productController.ts b/src/controllers/productController.ts index 11caddd..05aa5a3 100644 --- a/src/controllers/productController.ts +++ b/src/controllers/productController.ts @@ -1,26 +1,18 @@ import { Request, Response } from 'express'; import { - createProductService, - updateProductService, - - removeProductImageService, - + removeProductImageService, readProductService, - readProductsService, - + readProductsService, deleteProductService, - getRecommendedProductsService, productStatusServices, viewSingleProduct, - searchProductService - -, - listAllProductsService} -from '../services'; - + searchProductService, + listAllProductsService, + confirmPayment, +} from '../services'; export const readProduct = async (req: Request, res: Response) => { await readProductService(req, res); @@ -50,10 +42,10 @@ export const getRecommendedProducts = async (req: Request, res: Response) => { await getRecommendedProductsService(req, res); }; - export const listAllProducts = async (req: Request, res: Response) => { await listAllProductsService(req, res); -};export const productStatus = async (req: Request, res: Response) => { +}; +export const productStatus = async (req: Request, res: Response) => { await productStatusServices(req, res); }; export const singleProduct = async (req: Request, res: Response) => { @@ -79,3 +71,6 @@ export const searchProduct = async (req: Request, res: Response) => { res.status(500).json({ error: 'Internal Server Error' }); } }; +export const Payment = async (req: Request, res: Response) => { + await confirmPayment(req, res); +}; diff --git a/src/controllers/vendorOrderController.ts b/src/controllers/vendorOrderController.ts new file mode 100644 index 0000000..955b01c --- /dev/null +++ b/src/controllers/vendorOrderController.ts @@ -0,0 +1,14 @@ +import { Request, Response } from 'express'; +import { getVendorOrdersService, getSingleVendorOrderService, updateVendorOrderService } from '../services'; + +export const getVendorOrders = async (req: Request, res: Response) => { + await getVendorOrdersService(req, res); +}; + +export const getSingleVendorOrder = async (req: Request, res: Response) => { + await getSingleVendorOrderService(req, res); +}; + +export const updateVendorOrder = async (req: Request, res: Response) => { + await updateVendorOrderService(req, res); +}; diff --git a/src/controllers/wishListController.ts b/src/controllers/wishListController.ts index e0cd1bd..23fa03f 100644 --- a/src/controllers/wishListController.ts +++ b/src/controllers/wishListController.ts @@ -1,23 +1,18 @@ import { Request, Response } from 'express'; -import{ - addProductService, - getProductsService, - removeProductService, - clearAllProductService -} from '../services' +import { addProductService, getProductsService, removeProductService, clearAllProductService } from '../services'; export const wishlistAddProduct = async (req: Request, res: Response) => { - await addProductService(req, res); - }; + await addProductService(req, res); +}; - export const wishlistRemoveProduct = async (req: Request, res:Response) => { - await removeProductService(req, res); - } +export const wishlistRemoveProduct = async (req: Request, res: Response) => { + await removeProductService(req, res); +}; - export const wishlistGetProducts = async (req: Request, res:Response) => { - await getProductsService(req, res); - } +export const wishlistGetProducts = async (req: Request, res: Response) => { + await getProductsService(req, res); +}; - export const wishlistClearAllProducts = async (req: Request, res:Response) => { - await clearAllProductService(req, res); - } \ No newline at end of file +export const wishlistClearAllProducts = async (req: Request, res: Response) => { + await clearAllProductService(req, res); +}; diff --git a/src/docs/adminOrderManagement.yml b/src/docs/adminOrderManagement.yml new file mode 100644 index 0000000..e8d6ed6 --- /dev/null +++ b/src/docs/adminOrderManagement.yml @@ -0,0 +1,80 @@ +/product/admin/orders: + get: + tags: + - Admin Order Manangement + summary: Fetches all buyer and vendor orders + description: Return all order including details for buyer and vendors of products in that order + security: + - bearerAuth: [] + responses: + '200': + description: Return all order + '400': + description: Bad Request (syntax error, incorrect input format, etc..) + '401': + description: Unauthorized + '403': + description: Forbidden (Unauthorized action) + '500': + description: Internal server error + +/product/admin/orders/{id}: + get: + tags: + - Admin Order Manangement + summary: Fetch details for single buyer and vendor order + description: + Fetch details for single order using buyer id, if successful return order details with it's corresponding vendor + security: + - bearerAuth: [] + parameters: + - in: path + name: id + schema: + type: string + required: true + description: The id of a buyer order + responses: + '200': + description: Order details retrieved successfully + '400': + description: Bad Request (syntax error, incorrect input format, etc..) + '401': + description: Unauthorized + '403': + description: Forbidden (Unauthorized action) + '404': + description: Order not found + '500': + description: Internal server error + put: + tags: + - Admin Order Manangement + summary: Updates order status for both buyer and vendor order + description: Updates orderStatus field of order, if successful returns updated order. + security: + - bearerAuth: [] + parameters: + - in: path + name: id + schema: + type: string + required: true + description: The id of a buyer order + consumes: + - application/json + responses: + '200': + description: Order was successfully updated, return updated order + '400': + description: Bad Request (syntax error, incorrect input format, etc..) + '401': + description: Unauthorized + '403': + description: Forbidden (Unauthorized action) + '404': + description: Order not found + '409': + description: Order can not be updated because (it has already been completed(close), delivered, cancelled) + '500': + description: Internal server error diff --git a/src/docs/couponDocs.yml b/src/docs/couponDocs.yml index fb0a49a..fefa829 100644 --- a/src/docs/couponDocs.yml +++ b/src/docs/couponDocs.yml @@ -35,7 +35,7 @@ description: The code of the coupon responses: '200': - description: Return info for the coupon + description: Return info for the coupon '400': description: Bad Request (syntax error, incorrect input format, etc..) '401': diff --git a/src/docs/notifications.yml b/src/docs/notifications.yml new file mode 100644 index 0000000..61e240f --- /dev/null +++ b/src/docs/notifications.yml @@ -0,0 +1,126 @@ +/notification: + get: + tags: + - Notification Management + summary: Get all user notifications + description: Return all user notifications of an authenticated buyer + security: + - bearerAuth: [] + responses: + '200': + description: Return all user notifications for a user + '400': + description: Bad Request (syntax error, incorrect input format, etc..) + '401': + description: Unauthorized + '403': + description: Forbidden (Unauthorized action) + '500': + description: Internal server error + + delete: + tags: + - Notification Management + summary: Delete selected notifications + description: Delete all selected notification for an authenticated buyer + security: + - bearerAuth: [] + consumes: + - application/json + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + notificationIds: + example: "[]" + responses: + '200': + description: Notifications deleted successfully + '400': + description: Bad Request (syntax error, incorrect input format, etc..) + '401': + description: Unauthorized + '403': + description: Forbidden (Unauthorized action) + '404': + description: Notification not found + '500': + description: Internal server error + + put: + tags: + - Notification Management + summary: Update selected notifications + description: Update selected notifications for an authenticated buyer + security: + - bearerAuth: [] + consumes: + - application/json + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + notificationIds: + example: "[]" + responses: + '200': + description: Notification updated successfully + '400': + description: Bad Request (syntax error, incorrect input format, etc..) + '401': + description: Unauthorized + '403': + description: Forbidden (Unauthorized action) + '404': + description: Notification not found + '500': + description: Internal server error + +/notification/all: + delete: + tags: + - Notification Management + summary: Delete all notifications + description: Delete all notification for an authenticated buyer + security: + - bearerAuth: [] + responses: + '200': + description: All notifications deleted successfully + '400': + description: Bad Request (syntax error, incorrect input format, etc..) + '401': + description: Unauthorized + '403': + description: Forbidden (Unauthorized action) + '404': + description: Notification not found + '500': + description: Internal server error + + put: + tags: + - Notification Management + summary: Update all notifications + description: Update all notifications for an authenticated buyer + security: + - bearerAuth: [] + responses: + '200': + description: Notification updated successfully + '400': + description: Bad Request (syntax error, incorrect input format, etc..) + '401': + description: Unauthorized + '403': + description: Forbidden (Unauthorized action) + '404': + description: Notification not found + '500': + description: Internal server error \ No newline at end of file diff --git a/src/docs/orderDocs.yml b/src/docs/orderDocs.yml index fcb620e..c9f692a 100644 --- a/src/docs/orderDocs.yml +++ b/src/docs/orderDocs.yml @@ -106,3 +106,32 @@ paths: description: Order not found '500': description: Internal Server Error + + /product/client/orders/{orderId}: + get: + tags: + - Order + summary: Get a single order + description: Retrieve an order for the authenticated user + security: + - bearerAuth: [] + parameters: + - in: path + name: orderId + schema: + type: string + required: true + description: The ID of the order + responses: + '200': + description: Order Retrived successfully + '400': + description: Bad Request + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Order not found + '500': + description: Internal Server Error diff --git a/src/docs/vendorOrderManagement.yml b/src/docs/vendorOrderManagement.yml new file mode 100644 index 0000000..5873717 --- /dev/null +++ b/src/docs/vendorOrderManagement.yml @@ -0,0 +1,93 @@ +/product/vendor/orders: + get: + tags: + - Vendor Order Manangement + summary: Fetches all vendor orders + description: Return all order for authenticated vendor + security: + - bearerAuth: [] + responses: + '200': + description: Return all order for vendor requested from buyer + '400': + description: Bad Request (syntax error, incorrect input format, etc..) + '401': + description: Unauthorized + '403': + description: Forbidden (Unauthorized action) + '500': + description: Internal server error + +/product/vendor/orders/{id}: + get: + tags: + - Vendor Order Manangement + summary: Fetch details for single vendor order + description: + Fetch details for single order for authenticated vendor, order that include only his/her product which a buyer has + requested in his order. + security: + - bearerAuth: [] + parameters: + - in: path + name: id + schema: + type: string + required: true + description: The id of a vendor order + responses: + '200': + description: Order details retrieved successfully + '400': + description: Bad Request (syntax error, incorrect input format, etc..) + '401': + description: Unauthorized + '403': + description: Forbidden (Unauthorized action) + '404': + description: Order not found + '500': + description: Internal server error + put: + tags: + - Vendor Order Manangement + summary: Updates order status for vendor order + description: + Updates orderStatus field of vendor order for authenticated vendor, it order that include only his/her product + which a buyer has request in his order. + security: + - bearerAuth: [] + parameters: + - in: path + name: id + schema: + type: string + required: true + description: The id of a vendor order + consumes: + - application/json + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + orderStatus: + type: string + example: "'is-accepted', 'in-transit', 'cancelled', 'delivered'" + responses: + '200': + description: Order was successfully updated, return updated order + '400': + description: Bad Request (syntax error, incorrect input format, etc..) + '401': + description: Unauthorized + '403': + description: Forbidden (Unauthorized action) + '404': + description: Order not found + '409': + description: Order can not be updated because (it has already been completed(close), delivered, cancelled) + '500': + description: Internal server error diff --git a/src/docs/vendorProduct.yml b/src/docs/vendorProduct.yml index 937b097..3830bf4 100644 --- a/src/docs/vendorProduct.yml +++ b/src/docs/vendorProduct.yml @@ -35,7 +35,7 @@ description: The id of product responses: '200': - description: Return info for the product + description: Return info for the product '400': description: Bad Request (syntax error, incorrect input format, etc..) '401': @@ -59,7 +59,7 @@ requestBody: required: true content: - application/json: + application/json: schema: type: object properties: @@ -75,10 +75,10 @@ type: file categories: oneOf: - - type: string - - type: array - items: - type: string + - type: string + - type: array + items: + type: string example: "'category' or ['category1', 'category2', ...]" expirationDate: type: string @@ -159,7 +159,7 @@ description: Product not found '500': description: Internal server error - + /product/images/{id}: delete: tags: diff --git a/src/docs/wishListDocs.yml b/src/docs/wishListDocs.yml index df3c72c..7f705f7 100644 --- a/src/docs/wishListDocs.yml +++ b/src/docs/wishListDocs.yml @@ -94,4 +94,4 @@ '403': description: Forbidden (Unauthorized action) '500': - description: Internal server error \ No newline at end of file + description: Internal server error diff --git a/src/entities/Cart.ts b/src/entities/Cart.ts index 0ba44a6..fda1e15 100644 --- a/src/entities/Cart.ts +++ b/src/entities/Cart.ts @@ -36,7 +36,7 @@ export class Cart { @UpdateDateColumn() updatedAt!: Date; - updateTotal(): void { + updateTotal (): void { if (this.items) { let total: number = 0; for (let i = 0; i < this.items.length; i++) { diff --git a/src/entities/CartItem.ts b/src/entities/CartItem.ts index 107170c..d651adf 100644 --- a/src/entities/CartItem.ts +++ b/src/entities/CartItem.ts @@ -47,7 +47,7 @@ export class CartItem { @BeforeInsert() @BeforeUpdate() - updateTotal(): void { + updateTotal (): void { this.total = this.newPrice * this.quantity; } } diff --git a/src/entities/Feedback.ts b/src/entities/Feedback.ts new file mode 100644 index 0000000..6de9058 --- /dev/null +++ b/src/entities/Feedback.ts @@ -0,0 +1,30 @@ +import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, CreateDateColumn, UpdateDateColumn } from 'typeorm'; +import { User } from './User'; +import { Product } from './Product'; +import { IsNotEmpty } from 'class-validator'; +import { Order } from './Order'; + +@Entity() +export class Feedback { + @PrimaryGeneratedColumn('uuid') + @IsNotEmpty() + id!: string; + + @Column('text') + comment!: string; + + @ManyToOne(() => User, user => user.feedbacks) + user!: User; + + @ManyToOne(() => Product, product => product.feedbacks) + product!: Product; + + @ManyToOne(() => Order, order => order.feedbacks) + order!: Order; + + @CreateDateColumn() + createdAt!: Date; + + @UpdateDateColumn() + updatedAt!: Date; +} diff --git a/src/entities/Notification.ts b/src/entities/Notification.ts new file mode 100644 index 0000000..bc55b8b --- /dev/null +++ b/src/entities/Notification.ts @@ -0,0 +1,51 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + ManyToOne, + OneToMany, + CreateDateColumn, + UpdateDateColumn, +} from 'typeorm'; +import { IsNotEmpty } from 'class-validator'; +import { User } from './User'; +import { NotificationItem } from './NotificationItem'; + +@Entity() +export class Notification { + @PrimaryGeneratedColumn('uuid') + @IsNotEmpty() + id!: string; + + @ManyToOne(() => User, { + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }) + user!: User; + + @OneToMany(() => NotificationItem, notificationItem => notificationItem.notification) + allNotifications!: NotificationItem[]; + + @Column('decimal') + unRead: number = 0; + + @CreateDateColumn() + createdAt!: Date; + + @UpdateDateColumn() + updatedAt!: Date; + + updateUnread (): void { + if (this.allNotifications) { + let unRead: number = 0; + for (let i = 0; i < this.allNotifications.length; i++) { + if(this.allNotifications[i].isRead === false){ + unRead +=1 + } + } + this.unRead = unRead; + } else { + this.unRead = 0; + } + } +} \ No newline at end of file diff --git a/src/entities/NotificationItem.ts b/src/entities/NotificationItem.ts new file mode 100644 index 0000000..df5881a --- /dev/null +++ b/src/entities/NotificationItem.ts @@ -0,0 +1,47 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + ManyToOne, + CreateDateColumn, + } from 'typeorm'; +import { IsNotEmpty, IsIn, IsBoolean } from 'class-validator'; +import { Notification } from './Notification'; + +@Entity() +export class NotificationItem{ + @PrimaryGeneratedColumn('uuid') + @IsNotEmpty() + id!: string; + + @ManyToOne(() => Notification, nofication => nofication.allNotifications, { onDelete: 'CASCADE' }) + @IsNotEmpty() + notification!: Notification; + + @Column() + @IsNotEmpty() + content!: string + + @Column() + @IsNotEmpty() + @IsIn([ + 'product', + 'cart', + 'order', + 'user', + 'wish list', + 'coupon', + ]) + type!: string + + @Column({ default: false }) + @IsNotEmpty() + @IsBoolean() + isRead!: boolean; + + @Column({ nullable: true }) + link!: string; + + @CreateDateColumn() + createdAt!: Date; +} \ No newline at end of file diff --git a/src/entities/Order.ts b/src/entities/Order.ts index 49965a0..faa19db 100644 --- a/src/entities/Order.ts +++ b/src/entities/Order.ts @@ -1,9 +1,17 @@ -import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany, CreateDateColumn, UpdateDateColumn } from 'typeorm'; +import { + Entity, + PrimaryGeneratedColumn, + Column, + ManyToOne, + OneToMany, + CreateDateColumn, + UpdateDateColumn, +} from 'typeorm'; import { IsNotEmpty, IsNumber, IsDate, IsIn } from 'class-validator'; import { User } from './User'; import { OrderItem } from './OrderItem'; import { Transaction } from './transaction'; - +import { Feedback } from './Feedback'; @Entity() export class Order { @@ -24,11 +32,24 @@ export class Order { @IsNumber() totalPrice!: number; - @OneToMany(() => Transaction, (transaction) => transaction.order) + @OneToMany(() => Transaction, transaction => transaction.order) transactions!: Transaction[]; + + @OneToMany(() => Feedback, feedback => feedback.order) + feedbacks!: Feedback[]; + @Column({ default: 'order placed' }) @IsNotEmpty() - @IsIn(['order placed', 'cancelled', 'awaiting shipment', 'in transit', 'delivered', 'received', 'returned']) + @IsIn([ + 'order placed', + 'cancelled', + 'awaiting shipment', + 'in transit', + 'delivered', + 'received', + 'returned', + 'completed', + ]) orderStatus!: string; @Column('int') diff --git a/src/entities/Product.ts b/src/entities/Product.ts index 2b39493..ae027ef 100644 --- a/src/entities/Product.ts +++ b/src/entities/Product.ts @@ -18,6 +18,8 @@ import { Category } from './Category'; import { Order } from './Order'; import { Coupon } from './coupon'; import { OrderItem } from './OrderItem'; +import { VendorOrderItem } from './VendorOrderItem'; +import { Feedback } from './Feedback'; @Entity() @Unique(['id']) @@ -36,6 +38,11 @@ export class Product { @OneToMany(() => OrderItem, orderItem => orderItem.product) orderItems!: OrderItem[]; + @OneToMany(() => VendorOrderItem, vendorOrderItems => vendorOrderItems.product) + vendorOrderItems!: VendorOrderItem[]; + @OneToMany(() => Feedback, feedback => feedback.product) + feedbacks!: Feedback[]; + @OneToOne(() => Coupon, (coupons: any) => coupons.product) @JoinColumn() coupons?: Coupon; diff --git a/src/entities/User.ts b/src/entities/User.ts index fb45fe9..232ce11 100644 --- a/src/entities/User.ts +++ b/src/entities/User.ts @@ -1,3 +1,4 @@ + import { Entity, PrimaryGeneratedColumn, @@ -12,6 +13,7 @@ import { IsEmail, IsNotEmpty, IsString, IsBoolean, IsIn } from 'class-validator' import { roles } from '../utils/roles'; import { Order } from './Order'; import { Transaction } from './transaction'; +import { Feedback } from './Feedback'; export interface UserInterface { id?: string; @@ -99,7 +101,7 @@ export class User { @OneToMany(() => Order, (order: any) => order.buyer) orders!: Order[]; - @OneToMany(() => Transaction, (transaction) => transaction.user) + @OneToMany(() => Transaction, transaction => transaction.user) transactions!: Transaction[]; @CreateDateColumn() @@ -110,9 +112,11 @@ export class User { @Column({ type: 'numeric', precision: 24, scale: 2, default: 0 }) accountBalance!: number; + @OneToMany(() => Feedback, feedback => feedback.product) + feedbacks!: Feedback[]; @BeforeInsert() - setRole(): void { + setRole (): void { this.role = this.userType === 'Vendor' ? roles.vendor : roles.buyer; } } diff --git a/src/entities/VendorOrderItem.ts b/src/entities/VendorOrderItem.ts new file mode 100644 index 0000000..fc8b3dc --- /dev/null +++ b/src/entities/VendorOrderItem.ts @@ -0,0 +1,30 @@ +import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm'; +import { IsNotEmpty, IsNumber } from 'class-validator'; +import { Order } from './Order'; +import { Product } from './Product'; +import { VendorOrders } from './vendorOrders'; + +@Entity() +export class VendorOrderItem { + @PrimaryGeneratedColumn('uuid') + @IsNotEmpty() + 'id'!: string; + + @ManyToOne(() => VendorOrders, order => order.vendorOrderItems, {onDelete: 'CASCADE'}) + @IsNotEmpty() + 'order'!: VendorOrders; + + @ManyToOne(() => Product, product => product.vendorOrderItems, {onDelete: 'CASCADE'}) + @IsNotEmpty() + 'product'!: Product; + + @Column('decimal') + @IsNotEmpty() + @IsNumber() + 'price/unit'!: number; + + @Column('int') + @IsNotEmpty() + @IsNumber() + 'quantity'!: number; +} diff --git a/src/entities/vendorOrders.ts b/src/entities/vendorOrders.ts new file mode 100644 index 0000000..38269e6 --- /dev/null +++ b/src/entities/vendorOrders.ts @@ -0,0 +1,49 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + ManyToOne, + OneToMany, + CreateDateColumn, + UpdateDateColumn, +} from 'typeorm'; +import { IsNotEmpty, IsNumber, IsDate, IsIn, isNotEmpty } from 'class-validator'; +import { User } from './User'; +import { OrderItem } from './OrderItem'; +import { Transaction } from './transaction'; +import { Order } from './Order'; +import { VendorOrderItem } from './VendorOrderItem'; + +@Entity() +export class VendorOrders { + @PrimaryGeneratedColumn('uuid') + @IsNotEmpty() + id!: string; + + @ManyToOne(() => User) + @IsNotEmpty() + vendor!: User; + + @OneToMany(() => VendorOrderItem, vendorOrderItems => vendorOrderItems.order, { cascade: true }) + @IsNotEmpty() + vendorOrderItems!: VendorOrderItem[]; + + @ManyToOne(() => Order) + @IsNotEmpty() + order!: Order; + + @Column('decimal') + @IsNotEmpty() + @IsNumber() + totalPrice!: number; + + @Column({ default: 'pending' }) + @IsIn(['pending', 'is-accepted', 'in-transit', 'cancelled', 'delivered', 'completed']) + orderStatus!: string; + + @CreateDateColumn() + createdAt!: Date; + + @UpdateDateColumn() + updatedAt!: Date; +} diff --git a/src/entities/wishList.ts b/src/entities/wishList.ts index 69dbebd..7f74023 100644 --- a/src/entities/wishList.ts +++ b/src/entities/wishList.ts @@ -1,10 +1,19 @@ -import { Entity, PrimaryGeneratedColumn, BaseEntity,Column, Unique, ManyToOne, CreateDateColumn, UpdateDateColumn,} from "typeorm"; +import { + Entity, + PrimaryGeneratedColumn, + BaseEntity, + Column, + Unique, + ManyToOne, + CreateDateColumn, + UpdateDateColumn, +} from 'typeorm'; import { IsNotEmpty, IsString } from 'class-validator'; import { User } from './User'; -@Entity("wishlist") +@Entity('wishlist') @Unique(['id']) -export class wishList extends BaseEntity{ +export class wishList extends BaseEntity { @PrimaryGeneratedColumn() @IsNotEmpty() id!: number; @@ -23,4 +32,4 @@ export class wishList extends BaseEntity{ @UpdateDateColumn() updatedAt!: Date; -} \ No newline at end of file +} diff --git a/src/index.ts b/src/index.ts index 07efd39..2401347 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,15 +11,21 @@ import passport from 'passport'; import { CustomError, errorHandler } from './middlewares/errorHandler'; import morgan from 'morgan'; import { dbConnection } from './startups/dbConnection'; + +import { Server } from 'socket.io'; +import { init as initSocketIO } from './utils/socket'; + dotenv.config(); export const app = express(); const port = process.env.PORT || 8000; -app.use(session({ - secret: 'keyboard cat' -})) -app.use(passport.initialize()) -app.use(passport.session()) +app.use( + session({ + secret: 'keyboard cat', + }) +); +app.use(passport.initialize()); +app.use(passport.session()); app.use(express.json()); app.use(cookieParser()); app.use(cors({ origin: '*' })); @@ -43,3 +49,14 @@ app.use(morgan(morganFormat)); export const server = app.listen(port, () => { console.log(`[server]: Server is running at http://localhost:${port}`); }); + +// Socket.IO setup +const io = initSocketIO(server); + +io.on('connection', socket => { + console.log('Client connected'); + + socket.on('disconnect', () => { + console.log('Client disconnected'); + }); +}); diff --git a/src/node-nlp.d.ts b/src/node-nlp.d.ts new file mode 100644 index 0000000..8228fa6 --- /dev/null +++ b/src/node-nlp.d.ts @@ -0,0 +1 @@ +declare module 'node-nlp'; diff --git a/src/routes/NoficationRoutes.ts b/src/routes/NoficationRoutes.ts new file mode 100644 index 0000000..1e8d9a5 --- /dev/null +++ b/src/routes/NoficationRoutes.ts @@ -0,0 +1,17 @@ +import { RequestHandler, Router } from 'express'; +import { authMiddleware } from '../middlewares/verifyToken'; +import { getAllNotifications, updateNotifications, deleteSelectedNotifications, deleteAllNotifications, updateAllNotifications } from '../controllers/notificationControllers'; + +const router = Router(); + +router.get('/', authMiddleware as RequestHandler, getAllNotifications); + +router.put('/', authMiddleware as RequestHandler, updateNotifications); + +router.put('/all', authMiddleware as RequestHandler, updateAllNotifications); + +router.delete('/', authMiddleware as RequestHandler, deleteSelectedNotifications); + +router.delete('/all', authMiddleware as RequestHandler, deleteAllNotifications); + +export default router; \ No newline at end of file diff --git a/src/routes/ProductRoutes.ts b/src/routes/ProductRoutes.ts index ce146ec..b2af1a5 100644 --- a/src/routes/ProductRoutes.ts +++ b/src/routes/ProductRoutes.ts @@ -1,6 +1,6 @@ import { RequestHandler, Router } from 'express'; -import { productStatus, searchProduct } from '../controllers/index'; +import { productStatus, searchProduct, } from '../controllers/index'; import { hasRole } from '../middlewares/roleCheck'; import upload from '../middlewares/multer'; import { authMiddleware } from '../middlewares/verifyToken'; @@ -16,9 +16,15 @@ import { listAllProducts, singleProduct, createOrder, - getOrders, + getOrders, getOrder, updateOrder, - getOrdersHistory + getOrdersHistory,Payment, + getSingleVendorOrder, + getVendorOrders, + updateVendorOrder, + getBuyerVendorOrders, + getSingleBuyerVendorOrder, + updateBuyerVendorOrder, } from '../controllers'; const router = Router(); router.get('/all', listAllProducts); @@ -32,9 +38,22 @@ router.put('/:id', authMiddleware as RequestHandler, hasRole('VENDOR'), upload.a router.delete('/images/:id', authMiddleware as RequestHandler, hasRole('VENDOR'), removeProductImage); router.delete('/:id', authMiddleware as RequestHandler, hasRole('VENDOR'), deleteProduct); router.put('/availability/:id', authMiddleware as RequestHandler, hasRole('VENDOR'), productStatus); + router.post('/orders', authMiddleware as RequestHandler, hasRole('BUYER'), createOrder); router.get('/client/orders', authMiddleware as RequestHandler, hasRole('BUYER'), getOrders); +router.get('/client/orders/:orderId', authMiddleware as RequestHandler, hasRole('BUYER'), getOrder); router.put('/client/orders/:orderId', authMiddleware as RequestHandler, hasRole('BUYER'), updateOrder); router.get('/orders/history', authMiddleware as RequestHandler, hasRole('BUYER'), getOrdersHistory); +// Vendor order management +router.get('/vendor/orders', authMiddleware as RequestHandler, hasRole('VENDOR'), getVendorOrders); +router.get('/vendor/orders/:id', authMiddleware as RequestHandler, hasRole('VENDOR'), getSingleVendorOrder); +router.put('/vendor/orders/:id', authMiddleware as RequestHandler, hasRole('VENDOR'), updateVendorOrder); + +// Admin order management +router.get('/admin/orders', authMiddleware as RequestHandler, hasRole('ADMIN'), getBuyerVendorOrders); +router.get('/admin/orders/:id', authMiddleware as RequestHandler, hasRole('ADMIN'), getSingleBuyerVendorOrder); +router.put('/admin/orders/:id', authMiddleware as RequestHandler, hasRole('ADMIN'), updateBuyerVendorOrder); +router.post('/payment/:id', authMiddleware as RequestHandler, hasRole('BUYER'), Payment); + export default router; diff --git a/src/routes/UserRoutes.ts b/src/routes/UserRoutes.ts index 50bb4ca..79b0551 100644 --- a/src/routes/UserRoutes.ts +++ b/src/routes/UserRoutes.ts @@ -1,7 +1,7 @@ import { Router } from 'express'; import { responseError } from '../utils/response.utils'; import { UserInterface } from '../entities/User'; -import jwt from 'jsonwebtoken' +import jwt from 'jsonwebtoken'; import { disable2FA, enable2FA, @@ -19,7 +19,7 @@ import { activateUser, disactivateUser, userProfileUpdate } from '../controllers import { hasRole } from '../middlewares/roleCheck'; import { isTokenValide } from '../middlewares/isValid'; import passport from 'passport'; -import "../utils/auth"; +import '../utils/auth'; const router = Router(); router.post('/register', userRegistration); @@ -37,35 +37,36 @@ router.post('/password/reset/link', sendPasswordResetLink); router.put('/update', userProfileUpdate); router.get('/google-auth', passport.authenticate('google', { scope: ['profile', 'email'] })); -router.get("/auth/google/callback", - passport.authenticate("google", { - successRedirect: "/user/login/success", - failureRedirect: "/user/login/failed" +router.get( + '/auth/google/callback', + passport.authenticate('google', { + successRedirect: '/user/login/success', + failureRedirect: '/user/login/failed', }) ); -router.get("/login/success", async (req, res) => { +router.get('/login/success', async (req, res) => { const user = req.user as UserInterface; - if(!user){ - responseError(res, 404, 'user not found') + if (!user) { + responseError(res, 404, 'user not found'); } const payload = { id: user?.id, email: user?.email, - role: user?.role - } - const token = jwt.sign(payload, process.env.JWT_SECRET as string,{expiresIn: '24h'}) + role: user?.role, + }; + const token = jwt.sign(payload, process.env.JWT_SECRET as string, { expiresIn: '24h' }); res.status(200).json({ status: 'success', - data:{ - token: token, - message: "Login success" - } - }) + data: { + token: token, + message: 'Login success', + }, + }); }); -router.get("/login/failed", async (req, res) => { +router.get('/login/failed', async (req, res) => { res.status(401).json({ status: false, - message: "Login failed" + message: 'Login failed', }); }); diff --git a/src/routes/chatBot.ts b/src/routes/chatBot.ts new file mode 100644 index 0000000..f54638a --- /dev/null +++ b/src/routes/chatBot.ts @@ -0,0 +1,10 @@ +import { RequestHandler, Router } from 'express'; +import { chatBotController } from '../controllers/chatBotController'; +import { optinalAuthMiddleware } from '../middlewares/optionalAuthorization'; + +const router = Router(); + +router.post('/', optinalAuthMiddleware as RequestHandler, chatBotController); + + +export default router; diff --git a/src/routes/couponRoutes.ts b/src/routes/couponRoutes.ts index c315ab8..3378fbe 100644 --- a/src/routes/couponRoutes.ts +++ b/src/routes/couponRoutes.ts @@ -1,5 +1,12 @@ import { RequestHandler, Router } from 'express'; -import { createCoupon, updateCoupon, accessAllCoupon, readCoupon, deleteCoupon, buyerApplyCoupon } from '../controllers/couponController'; +import { + createCoupon, + updateCoupon, + accessAllCoupon, + readCoupon, + deleteCoupon, + buyerApplyCoupon, +} from '../controllers/couponController'; import { hasRole } from '../middlewares/roleCheck'; import { authMiddleware } from '../middlewares/verifyToken'; @@ -10,6 +17,6 @@ router.put('/vendor/:id/update-coupon/:code', authMiddleware as RequestHandler, router.get('/vendor/:id/checkout/:code', authMiddleware as RequestHandler, hasRole('VENDOR'), readCoupon); router.get('/vendor/:id/access-coupons', authMiddleware as RequestHandler, hasRole('VENDOR'), accessAllCoupon); router.delete('/vendor/:id/checkout/delete', authMiddleware as RequestHandler, hasRole('VENDOR'), deleteCoupon); -router.post('/apply', authMiddleware as RequestHandler, hasRole('BUYER'),buyerApplyCoupon); +router.post('/apply', authMiddleware as RequestHandler, hasRole('BUYER'), buyerApplyCoupon); -export default router; \ No newline at end of file +export default router; diff --git a/src/routes/feedbackRoutes.ts b/src/routes/feedbackRoutes.ts new file mode 100644 index 0000000..3ada81b --- /dev/null +++ b/src/routes/feedbackRoutes.ts @@ -0,0 +1,19 @@ +import { RequestHandler, Router } from 'express'; +import { + createFeedback, + updateFeedback, + deleteFeedback, + adminDeleteFeedback +} from '../controllers/feedbackController' +import { authMiddleware } from '../middlewares/verifyToken'; +import { hasRole } from '../middlewares/roleCheck'; + + +const router = Router(); + +router.post('/:productId/new', authMiddleware as RequestHandler, hasRole('BUYER'), createFeedback); +router.put('/update/:feedbackId', authMiddleware as RequestHandler, hasRole('BUYER'), updateFeedback ); +router.delete('/delete/:feedbackId', authMiddleware as RequestHandler, hasRole('BUYER'), deleteFeedback); +router.delete('/admin/delete/:feedbackId', authMiddleware as RequestHandler, hasRole('ADMIN'), adminDeleteFeedback ); + +export default router; diff --git a/src/routes/index.ts b/src/routes/index.ts index cddc08a..af462b4 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -3,8 +3,11 @@ import { responseSuccess } from '../utils/response.utils'; import userRoutes from './UserRoutes'; import productRoutes from './ProductRoutes'; import wishListRoutes from './wishListRoute'; -import couponRoute from './couponRoutes';; +import couponRoute from './couponRoutes'; import cartRoutes from './CartRoutes'; +import feedbackRoute from './feedbackRoutes'; +import notificationRoute from './NoficationRoutes' +import chatBot from './chatBot'; const router = Router(); @@ -14,8 +17,11 @@ router.get('/', (req: Request, res: Response) => { router.use('/user', userRoutes); router.use('/product', productRoutes); -router.use('/wish-list', wishListRoutes); +router.use('/wish-list', wishListRoutes); router.use('/cart', cartRoutes); router.use('/coupons', couponRoute); +router.use('/feedback', feedbackRoute); +router.use('/notification', notificationRoute); +router.use('/chat', chatBot); export default router; diff --git a/src/routes/wishListRoute.ts b/src/routes/wishListRoute.ts index d5ac6fb..ea96e40 100644 --- a/src/routes/wishListRoute.ts +++ b/src/routes/wishListRoute.ts @@ -2,13 +2,30 @@ import { RequestHandler, Router } from 'express'; import { authMiddleware } from '../middlewares/verifyToken'; import { hasRole } from '../middlewares'; import { checkUserStatus } from '../middlewares/isAllowed'; -import { wishlistAddProduct,wishlistRemoveProduct,wishlistGetProducts,wishlistClearAllProducts } from '../controllers/wishListController'; +import { + wishlistAddProduct, + wishlistRemoveProduct, + wishlistGetProducts, + wishlistClearAllProducts, +} from '../controllers/wishListController'; const router = Router(); router.post('/add/:id', authMiddleware as RequestHandler, checkUserStatus, hasRole('BUYER'), wishlistAddProduct); -router.get('/',authMiddleware as RequestHandler, checkUserStatus, hasRole('BUYER'),wishlistGetProducts); -router.delete('/delete/:id',authMiddleware as RequestHandler, checkUserStatus, hasRole('BUYER'),wishlistRemoveProduct); -router.delete('/clearAll',authMiddleware as RequestHandler, checkUserStatus, hasRole('BUYER'),wishlistClearAllProducts); +router.get('/', authMiddleware as RequestHandler, checkUserStatus, hasRole('BUYER'), wishlistGetProducts); +router.delete( + '/delete/:id', + authMiddleware as RequestHandler, + checkUserStatus, + hasRole('BUYER'), + wishlistRemoveProduct +); +router.delete( + '/clearAll', + authMiddleware as RequestHandler, + checkUserStatus, + hasRole('BUYER'), + wishlistClearAllProducts +); -export default router; \ No newline at end of file +export default router; diff --git a/src/services/adminOrderServices/readOrder.ts b/src/services/adminOrderServices/readOrder.ts new file mode 100644 index 0000000..4bb20f0 --- /dev/null +++ b/src/services/adminOrderServices/readOrder.ts @@ -0,0 +1,158 @@ +import { Request, Response } from 'express'; +import { getRepository } from 'typeorm'; +import { responseSuccess, responseError } from '../../utils/response.utils'; +import { VendorOrderItem } from '../../entities/VendorOrderItem'; +import { VendorOrders } from '../../entities/vendorOrders'; +import { Order } from '../../entities/Order'; + +export const getBuyerVendorOrdersService = async (req: Request, res: Response) => { + try { + const vendorOrderRepository = getRepository(VendorOrders); + const orderRepository = getRepository(Order); + + const orders = await orderRepository.find({ + relations: ['buyer', 'orderItems'], + order: { + createdAt: 'DESC', // Order by creation date, most recent first + }, + }); + + if (!orders.length) { + return responseError(res, 200, `There is no pending orders from buyer`, { orders: [] }); + } + + const sanitizedOrdersResponse = []; + + for (const order of orders) { + const vendorOrders = await vendorOrderRepository.find({ + where: { + order: { + id: order.id, + }, + }, + relations: ['vendor', 'vendorOrderItems', 'vendorOrderItems.product'], + order: { + createdAt: 'DESC', // Order by creation date, most recent first + }, + }); + + sanitizedOrdersResponse.push({ + id: order.id, + totalPrice: order.totalPrice, + totalProducts: order.orderItems.length, + orderStatus: order.orderStatus, + address: order.address, + createdAt: order.createdAt, + updatedAt: order.updatedAt, + buyer: { + id: order.buyer.id, + firstName: order.buyer.firstName, + lastName: order.buyer.lastName, + email: order.buyer.lastName, + gender: order.buyer.gender, + phoneNumber: order.buyer.phoneNumber, + photoUrl: order.buyer.photoUrl, + }, + vendors: vendorOrders.map(vendoOrder => ({ + id: vendoOrder.vendor.id, + firstName: vendoOrder.vendor.firstName, + lastName: vendoOrder.vendor.lastName, + email: vendoOrder.vendor.lastName, + gender: vendoOrder.vendor.gender, + phoneNumber: vendoOrder.vendor.phoneNumber, + photoUrl: vendoOrder.vendor.photoUrl, + order: { + id: vendoOrder.id, + totalPrice: vendoOrder.totalPrice, + orderStatus: vendoOrder.orderStatus, + createdAt: order.createdAt, + updatedAt: order.updatedAt, + orderItems: vendoOrder.vendorOrderItems, + }, + })), + }); + } + + responseSuccess(res, 200, 'Orders retrieved successfully', { + totalOrders: orders.length, + orders: sanitizedOrdersResponse, + }); + } catch (error) { + return responseError(res, 400, (error as Error).message); + } +}; + +// Get single vendor order info +export const getSingleBuyerVendorOrderService = async (req: Request, res: Response) => { + try { + const orderId = req.params.id; + + const vendorOrderRepository = getRepository(VendorOrders); + const orderRepository = getRepository(Order); + + const order = await orderRepository.findOne({ + where: { + id: orderId, + }, + relations: ['buyer', 'orderItems'], + order: { + createdAt: 'DESC', // Order by creation date, most recent first + }, + }); + + if (!order) { + return responseError(res, 404, `Order Not Found.`); + } + + const vendorOrders = await vendorOrderRepository.find({ + where: { + order: { + id: order.id, + }, + }, + relations: ['vendor', 'vendorOrderItems', 'vendorOrderItems.product'], + }); + + const sanitizedOrderResponse = { + id: order.id, + totalPrice: order.totalPrice, + totalProducts: order.orderItems.length, + orderStatus: order.orderStatus, + address: order.address, + createdAt: order.createdAt, + updatedAt: order.updatedAt, + buyer: { + id: order.buyer.id, + firstName: order.buyer.firstName, + lastName: order.buyer.lastName, + email: order.buyer.lastName, + gender: order.buyer.gender, + phoneNumber: order.buyer.phoneNumber, + photoUrl: order.buyer.photoUrl, + }, + vendors: vendorOrders.map(vendoOrder => ({ + id: vendoOrder.vendor.id, + firstName: vendoOrder.vendor.firstName, + lastName: vendoOrder.vendor.lastName, + email: vendoOrder.vendor.lastName, + gender: vendoOrder.vendor.gender, + phoneNumber: vendoOrder.vendor.phoneNumber, + photoUrl: vendoOrder.vendor.photoUrl, + order: { + id: vendoOrder.id, + totalPrice: vendoOrder.totalPrice, + orderStatus: vendoOrder.orderStatus, + createdAt: order.createdAt, + updatedAt: order.updatedAt, + orderItems: vendoOrder.vendorOrderItems, + }, + })), + }; + + responseSuccess(res, 200, 'Order retrieved successfully', { + order: sanitizedOrderResponse, + }); + } catch (error) { + return responseError(res, 400, (error as Error).message); + } +}; diff --git a/src/services/adminOrderServices/updateOrder.ts b/src/services/adminOrderServices/updateOrder.ts new file mode 100644 index 0000000..b6cdb2e --- /dev/null +++ b/src/services/adminOrderServices/updateOrder.ts @@ -0,0 +1,123 @@ +import { Request, Response } from 'express'; +import { Not, getRepository } from 'typeorm'; +import { responseSuccess, responseError } from '../../utils/response.utils'; +import { VendorOrderItem } from '../../entities/VendorOrderItem'; +import { sendNotification } from '../../utils/sendNotification'; +import { VendorOrders } from '../../entities/vendorOrders'; +import { Order } from '../../entities/Order'; +import { getIO } from '../../utils/socket'; +import { getNotifications } from '../../utils/getNotifications'; + +export const updateBuyerVendorOrderService = async (req: Request, res: Response) => { + try { + const orderId = req.params.id; + + const vendorOrderRepository = getRepository(VendorOrders); + const orderRepository = getRepository(Order); + + const order = await orderRepository.findOne({ + where: { + id: orderId, + }, + relations: ['buyer', 'orderItems'], + }); + + if (!order) { + return responseError(res, 404, `Order Not Found.`); + } + + if (order.orderStatus === 'completed') { + return responseError(res, 409, 'The order has already been completed.'); + } + + if (order.orderStatus !== 'received') { + return responseError(res, 409, 'Order closure failed: The buyer has not received the item yet.'); + } + + const vendorOrders = await vendorOrderRepository.find({ + where: { + order: { + id: order.id, + }, + }, + relations: ['vendor','order.buyer','vendorOrderItems', 'vendorOrderItems.product'], + }); + + for (const order of vendorOrders) { + if (order.orderStatus !== 'delivered') { + return responseError(res, 409, 'Order closure failed: Some vendors have not yet delivered items to the buyer.'); + } + } + + // Update Whole Order + + order.orderStatus = 'completed'; + await orderRepository.save(order); + + await sendNotification({ + content: 'Your order was marked completed', + type: 'order', + user: order.buyer, + link: `/product/client/orders/${order.id}` + }); + + const updatedVendorOrder = vendorOrders.map(async order => { + order.orderStatus = 'completed'; + await vendorOrderRepository.save(order); + + await sendNotification({ + content:`Order from buyer "${order.order.buyer.firstName} ${order.order.buyer.lastName}" has been marked completed`, + type: 'order', + user: order.vendor, + link: `/product/vendor/orders/${order.id}` + }); + }); + + const sanitizedOrderResponse = { + id: order.id, + totalPrice: order.totalPrice, + totalProducts: order.orderItems.length, + orderStatus: order.orderStatus, + address: order.address, + createdAt: order.createdAt, + updatedAt: order.updatedAt, + buyer: { + id: order.buyer.id, + firstName: order.buyer.firstName, + lastName: order.buyer.lastName, + email: order.buyer.lastName, + gender: order.buyer.gender, + phoneNumber: order.buyer.phoneNumber, + photoUrl: order.buyer.photoUrl, + }, + vendors: vendorOrders.map(vendoOrder => ({ + id: vendoOrder.vendor.id, + firstName: vendoOrder.vendor.firstName, + lastName: vendoOrder.vendor.lastName, + email: vendoOrder.vendor.lastName, + gender: vendoOrder.vendor.gender, + phoneNumber: vendoOrder.vendor.phoneNumber, + photoUrl: vendoOrder.vendor.photoUrl, + order: { + id: vendoOrder.id, + totalPrice: vendoOrder.totalPrice, + orderStatus: vendoOrder.orderStatus, + createdAt: order.createdAt, + updatedAt: order.updatedAt, + orderItems: vendoOrder.vendorOrderItems, + }, + })), + }; + + getIO().emit('orders', { + action: 'admin update', + order: sanitizedOrderResponse, + }); + + responseSuccess(res, 200, 'Order updated successfully', { + order: sanitizedOrderResponse, + }); + } catch (error) { + return responseError(res, 400, (error as Error).message); + } +}; diff --git a/src/services/chatbotServices/chatBot.ts b/src/services/chatbotServices/chatBot.ts new file mode 100644 index 0000000..44b2e37 --- /dev/null +++ b/src/services/chatbotServices/chatBot.ts @@ -0,0 +1,30 @@ +import { configDotenv } from 'dotenv'; +import { Request, Response } from 'express'; +import { sendSuccessResponse, sendErrorResponse } from '../../utils/response.utils'; +import { manager } from '../../train'; + + +export const chatBot = async (req: Request, res: Response) => { + const userMessage = req.body.message; + + try { + if(!userMessage){ + return sendErrorResponse(res, 400, 'No user message'); + } + const result = await manager.process('en', userMessage); + const intent = result.intent; + + if (result.answer || intent !== 'None') { + return sendSuccessResponse(res, 200, "", result.answer) + } + + else { + return sendSuccessResponse(res, 200, "Sorry, I am not sure what you mean. Can you rephrase?", result.answer) + } + + } catch (error) { + console.error(error); + return sendErrorResponse(res, 500, (error as Error).message); + + } + }; \ No newline at end of file diff --git a/src/services/couponServices/buyerApplyCoupon.ts b/src/services/couponServices/buyerApplyCoupon.ts index 12da4e1..93fa208 100644 --- a/src/services/couponServices/buyerApplyCoupon.ts +++ b/src/services/couponServices/buyerApplyCoupon.ts @@ -3,83 +3,99 @@ import { getRepository } from 'typeorm'; import { Coupon } from '../../entities/coupon'; import { Cart } from '../../entities/Cart'; import { CartItem } from '../../entities/CartItem'; +import { sendNotification } from '../../utils/sendNotification'; +import { responseSuccess, responseError } from '../../utils/response.utils'; export const buyerApplyCouponService = async (req: Request, res: Response) => { - try { - const {couponCode} = req.body + try { + const { couponCode } = req.body; - if (!couponCode) return res.status(400).json({ message: 'Coupon Code is required' }); + if (!couponCode) return res.status(400).json({ message: 'Coupon Code is required' }); - const couponRepository = getRepository(Coupon); - const coupon = await couponRepository.findOne({ - where: { code: couponCode }, - relations: ['product'], - }); - - if(!coupon) return res.status(404).json({message: 'Invalid Coupon Code'}); - - if(coupon){ - if(coupon.expirationDate && coupon.expirationDate < new Date()){ - return res.status(400).json({message: 'Coupon is expired'}); - } - - if(coupon.usageTimes == coupon.maxUsageLimit){ - return res.status(400).json({message: 'Coupon Discount Ended'}); - } - } - const couponProductId = coupon.product.id; - - const cartRepository = getRepository(Cart) - let cart = await cartRepository.findOne({where: { user: { id: req.user?.id },isCheckedOut: false }, - relations: ['items', 'items.product'], - }); - - if(!cart) return res.status(400).json({message: "You don't have a product in cart"}); - - const cartItemRepository = getRepository(CartItem); - const couponCartItem = await cartItemRepository.findOne({ - where: { - cart: { id: cart.id }, - product: { id: couponProductId }, - }, - relations: ['product'], - }); - - if(!couponCartItem) return res.status(404).json({message: 'No product in Cart with that coupon code'}); + const couponRepository = getRepository(Coupon); + const coupon = await couponRepository.findOne({ + where: { code: couponCode }, + relations: ['product', 'vendor'], + }); - let amountReducted; - if(coupon.discountType === 'percentage'){ - const reduction = (couponCartItem.product.newPrice * coupon.discountRate)/ 100; - amountReducted = reduction; - couponCartItem.newPrice = couponCartItem.product.newPrice - reduction; + if (!coupon) return res.status(404).json({ message: 'Invalid Coupon Code' }); - await cartItemRepository.save(couponCartItem) - } - else { - amountReducted = coupon.discountRate; - couponCartItem.newPrice = couponCartItem.product.newPrice - amountReducted; - await cartItemRepository.save(couponCartItem) + if (coupon) { + if (coupon.expirationDate && coupon.expirationDate < new Date()) { + return res.status(400).json({ message: 'Coupon is expired' }); } - cart = await cartRepository.findOne({where: { id: cart.id}, - relations: ['items', 'items.product'], - }); - if(cart){ - cart.updateTotal(); - await cartRepository.save(cart); + if (coupon.usageTimes == coupon.maxUsageLimit) { + return res.status(400).json({ message: 'Coupon Discount Ended' }); } + } + const couponProductId = coupon.product.id; + + const cartRepository = getRepository(Cart); + let cart = await cartRepository.findOne({ + where: { user: { id: req.user?.id }, isCheckedOut: false }, + relations: ['items', 'items.product', 'user'], + }); + + if (!cart) return res.status(400).json({ message: "You don't have a product in cart" }); + + const cartItemRepository = getRepository(CartItem); + const couponCartItem = await cartItemRepository.findOne({ + where: { + cart: { id: cart.id }, + product: { id: couponProductId }, + }, + relations: ['product'], + }); + + if (!couponCartItem) return res.status(404).json({ message: 'No product in Cart with that coupon code' }); + + let amountReducted; + if (coupon.discountType === 'percentage') { + const reduction = (couponCartItem.product.newPrice * coupon.discountRate) / 100; + amountReducted = reduction; + couponCartItem.newPrice = couponCartItem.product.newPrice - reduction; + + await cartItemRepository.save(couponCartItem); + } else { + amountReducted = coupon.discountRate; + couponCartItem.newPrice = couponCartItem.product.newPrice - amountReducted; + await cartItemRepository.save(couponCartItem); + } - coupon.usageTimes +=1; - - if(req.user?.id){ - coupon.usedBy.push(req.user?.id); - } + cart = await cartRepository.findOne({ where: { id: cart.id }, relations: ['items', 'items.product', 'user'] }); + if (!cart) return; - await couponRepository.save(coupon); + cart.updateTotal(); + await cartRepository.save(cart); + coupon.usageTimes += 1; - return (res.status(200).json({message: `Coupon Code successfully activated discount on product: ${couponCartItem.product.name}`, amountDiscounted: amountReducted })); + if (req.user?.id) { + coupon.usedBy.push(req.user?.id); + } - } catch (error) { - return res.status(500).json({ error: 'Internal server error' }); + await couponRepository.save(coupon); + + await sendNotification({ + content: `Coupon Code successfully activated discount on product: ${couponCartItem.product.name}`, + type: 'coupon', + user: cart.user + }) + + await sendNotification({ + content: `Buyer: "${cart?.user.firstName} ${cart?.user.lastName}" used coupon and got discount on product: "${couponCartItem.product.name}"`, + type:'coupon', + user: coupon.vendor, + link: `/coupons/vendor/${coupon.vendor.id}/checkout/${couponCode}` + }); + + return res + .status(200) + .json({ + message: `Coupon Code successfully activated discount on product: ${couponCartItem.product.name}`, + amountDiscounted: amountReducted, + }); + } catch (error) { + return responseError(res, 500, (error as Error).message); } - }; \ No newline at end of file +}; diff --git a/src/services/couponServices/createCouponService.ts b/src/services/couponServices/createCouponService.ts index a824ddf..592e462 100644 --- a/src/services/couponServices/createCouponService.ts +++ b/src/services/couponServices/createCouponService.ts @@ -10,7 +10,6 @@ export const createCouponService = async (req: Request, res: Response) => { try { const { error } = validateCoupon(req.body); if (error) { - console.log('Validation Error creating coupon:\n', error); return res.status(400).json({ status: 'error', error: error?.details[0].message }); } diff --git a/src/services/feedbackServices/adminDeleteFeedback.ts b/src/services/feedbackServices/adminDeleteFeedback.ts new file mode 100644 index 0000000..7bf6261 --- /dev/null +++ b/src/services/feedbackServices/adminDeleteFeedback.ts @@ -0,0 +1,25 @@ +import { Request, Response } from 'express'; +import { getRepository } from 'typeorm'; +import { Feedback } from '../../entities/Feedback'; +import { responseError, responseSuccess } from '../../utils/response.utils'; + +export const adminDeleteFeedbackService = async (req: Request, res: Response) => { + const { feedbackId } = req.params; + + try { + const feedbackRepository = getRepository(Feedback); + const feedback = await feedbackRepository.findOne({ + where: { id: feedbackId }, + }); + + if (!feedback) { + return responseError(res, 404, 'Feedback not found'); + } + + await feedbackRepository.remove(feedback); + + return responseSuccess(res, 200, 'Feedback successfully removed'); + } catch (error) { + return responseError(res, 500, 'Server error'); + } +}; diff --git a/src/services/feedbackServices/createFeedback.ts b/src/services/feedbackServices/createFeedback.ts new file mode 100644 index 0000000..8956bb7 --- /dev/null +++ b/src/services/feedbackServices/createFeedback.ts @@ -0,0 +1,52 @@ +import { Request, Response } from 'express'; +import { getRepository } from 'typeorm'; +import { Feedback } from '../../entities/Feedback'; +import { Product } from '../../entities/Product'; +import { User } from '../../entities/User'; +import { responseError, responseSuccess } from '../../utils/response.utils'; +import { Order } from '../../entities/Order'; +import { sendNotification } from '../../utils/sendNotification'; + +interface AuthRequest extends Request { + user?: User; +} + +export const createFeedbackService = async (req: Request, res: Response) => { + const { productId } = req.params; + const { comment, orderId } = req.body; + + try { + const feedbackRepository = getRepository(Feedback); + const productRepository = getRepository(Product); + const orderRepository = getRepository(Order); + if (!orderId) { + return responseError(res, 404, `Your feedback can't be recorded at this time Your order doesn't exist `); + } + const product = await productRepository.findOne({ where: { id: productId }, relations: ['vendor'] }); + if (!product) { + return responseError(res, 404, `Your feedback can't be recorded at this time product not found`); + } + const order = await orderRepository.findOne({ where: {id: orderId, orderStatus: 'completed', buyer: { id: req.user?.id }, orderItems: { product: { id: productId } }}, relations: ['buyer'] }) + if (!order) { + return responseError(res, 404, `Your feedback can't be recorded at this time Your order haven't been completed yet or doesn't contain this product`); + } + + const feedback = new Feedback(); + feedback.comment = comment; + feedback.user = req.user as User; + feedback.product = product; + + await feedbackRepository.save(feedback); + + await sendNotification({ + content: `Buyer: "${order.buyer.firstName} ${order.buyer.lastName}" sent feedback on product: ${product.name}`, + type: "product", + user: product.vendor, + link: `/product/collection/${product.id}` + }) + + return responseSuccess(res, 201, 'Feedback created successfully', feedback); + } catch (error) { + return responseError(res, 500, 'Server error'); + } +}; \ No newline at end of file diff --git a/src/services/feedbackServices/deleteFeedback.ts b/src/services/feedbackServices/deleteFeedback.ts new file mode 100644 index 0000000..5de4ea0 --- /dev/null +++ b/src/services/feedbackServices/deleteFeedback.ts @@ -0,0 +1,27 @@ +import { Request, Response } from 'express'; +import { getRepository } from 'typeorm'; +import { Feedback } from '../../entities/Feedback'; +import { responseError, responseSuccess } from '../../utils/response.utils'; + +export const deleteFeedbackService = async (req: Request, res: Response) => { + const { feedbackId } = req.params; + + try { + const feedbackRepository = getRepository(Feedback); + const feedback = await feedbackRepository.findOne({ + where: { id: feedbackId, + user: {id: req?.user?.id }, + } + }); + + if (!feedback) { + return responseError(res, 404, 'Feedback not found'); + } + + await feedbackRepository.remove(feedback); + + return responseSuccess(res, 200, 'Feedback successfully removed'); + } catch (error) { + return responseError(res, 500, 'Server error'); + } +}; diff --git a/src/services/feedbackServices/updateFeedback.ts b/src/services/feedbackServices/updateFeedback.ts new file mode 100644 index 0000000..18258c2 --- /dev/null +++ b/src/services/feedbackServices/updateFeedback.ts @@ -0,0 +1,32 @@ +import { Request, Response } from 'express'; +import { getRepository } from 'typeorm'; +import { Feedback } from '../../entities/Feedback'; +import { responseError, responseSuccess } from '../../utils/response.utils'; +import { User } from '../../entities/User'; + +export const updateFeedbackService = async (req: Request, res: Response) => { + const { feedbackId } = req.params; + const { comment } = req.body; + + try { + const feedbackRepository = getRepository(Feedback); + + const feedback = await feedbackRepository.findOne({ + where: { + id: feedbackId, + user: { id: req?.user?.id }, + }, + }); + + if (!feedback) { + return responseError(res, 404, 'You are not allowed to remove this feedback or you are not allowed to edit this feedback'); + } + + feedback.comment = comment; + await feedbackRepository.save(feedback); + + return responseSuccess(res, 200, 'Feedback updated successfully', feedback); + } catch (error) { + return responseError(res, 500, 'Server error'); + } +}; diff --git a/src/services/index.ts b/src/services/index.ts index 8f560c3..f31e750 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -21,6 +21,7 @@ export * from './productServices/listAllProductsService'; export * from './productServices/productStatus'; export * from './productServices/viewSingleProduct'; export * from './productServices/searchProduct'; +export * from './productServices/payment'; // Buyer wishlist services export * from './wishListServices/addProduct'; @@ -33,3 +34,19 @@ export * from './cartServices/createCart'; export * from './cartServices/readCart'; export * from './cartServices/removeProductInCart'; export * from './cartServices/clearCart'; + +// vendor order management +export * from './vendorOrderServices/readVendorOrder'; +export * from './vendorOrderServices/updateVendorOrder'; + +// vendor order management +export * from './adminOrderServices/readOrder'; +export * from './adminOrderServices/updateOrder'; + +// Nofication management +export * from './notificationServices/getNotifications'; +export * from './notificationServices/deleteNotification'; +export * from './notificationServices/updateNotification'; + +// chatbot +export * from './chatbotServices/chatBot'; \ No newline at end of file diff --git a/src/services/notificationServices/deleteNotification.ts b/src/services/notificationServices/deleteNotification.ts new file mode 100644 index 0000000..cc3c295 --- /dev/null +++ b/src/services/notificationServices/deleteNotification.ts @@ -0,0 +1,96 @@ +import { Request, Response } from 'express'; +import { Notification } from '../../entities/Notification'; +import { NotificationItem } from '../../entities/NotificationItem'; +import { responseSuccess, responseError } from '../../utils/response.utils'; +import { getRepository } from 'typeorm'; +import { getIO } from '../../utils/socket'; +import { getNotifications } from '../../utils/getNotifications'; + +export const deleteSelectedNotificationService = async (req: Request, res: Response) => { + try { + let notificationIds: string[] = req.body.notificationIds; + notificationIds = Array.from(new Set(notificationIds)); + + if (!notificationIds.length) { + return responseError(res, 400, 'Please provide Notification IDs to delete'); + } + + const notificationRepo = getRepository(Notification); + const notificationItemRepo = getRepository(NotificationItem); + + const notificationItems: string[] = []; + + for (const id of notificationIds) { + try { + const notificationItem = await notificationItemRepo.findOne({ + where: { + id: id, + notification: { + user: { + id: req.user?.id + } + } + } + }); + + if (notificationItem) { + await notificationItemRepo.remove(notificationItem); + notificationItems.push(id); + } + } catch (error) { + continue; + } + } + + const notification = await notificationRepo + .findOne({ + where: { user: { id: req.user?.id } }, + relations: ['allNotifications'] + }); + + if (notification) { + notification.updateUnread(); + await notificationRepo.save(notification); + } + + getIO().emit('notification', { + action: `${req.user?.email} notification`, + notifications: await getNotifications(req.user!.id!) + }); + + return responseSuccess(res, 200, `${notificationItems.length} of ${notificationIds.length} Notification(s) was successfully deleted.`); + + } catch (error) { + return responseError(res, 500, (error as Error).message); + } +}; + +export const deleteAllNotificationService = async (req: Request, res: Response) => { + try { + const notificationRepo = getRepository(Notification); + + + const notification = await notificationRepo + .findOne({ + where: { user: { id: req.user?.id } }, + relations: ['allNotifications', 'user'] + }); + + if (!notification || !notification.allNotifications.length ) { + responseError(res, 404, "User doesn't have notifications"); + return; + } + + await notificationRepo.remove(notification); + + getIO().emit('notification', { + action: `${req.user?.email} notification`, + notifications: await getNotifications(req.user!.id!) + }); + + return responseSuccess(res, 200, `All Notifications was successfully deleted.`); + + } catch (error) { + return responseError(res, 500, (error as Error).message); + } +}; diff --git a/src/services/notificationServices/getNotifications.ts b/src/services/notificationServices/getNotifications.ts new file mode 100644 index 0000000..a291c33 --- /dev/null +++ b/src/services/notificationServices/getNotifications.ts @@ -0,0 +1,31 @@ +import { Request, Response } from 'express'; +import { Notification } from '../../entities/Notification'; +import { responseSuccess, responseError } from '../../utils/response.utils'; +import { getRepository } from 'typeorm'; + +export const getNotificationsService = async(req: Request, res: Response) => { + try { + const notificationRepo = getRepository(Notification); + + const notification = await notificationRepo + .findOne({where: {user: { id: req.user?.id }}, relations: ['user','allNotifications'], + order: { + createdAt: 'DESC', + }, + }); + + if(!notification){ + return responseSuccess(res, 200, `User doesn't have any notifications.`, { notificationDetails: {} }); + } + + const notificationDetails = { + id: notification.id, + notifications: notification.allNotifications, + unRead: notification.unRead + }; + + return responseSuccess(res, 200, 'Notifications retrieved successfully', { notificationDetails }); + } catch (error) { + return responseError(res, 500, (error as Error).message); + } +}; \ No newline at end of file diff --git a/src/services/notificationServices/updateNotification.ts b/src/services/notificationServices/updateNotification.ts new file mode 100644 index 0000000..c8295d9 --- /dev/null +++ b/src/services/notificationServices/updateNotification.ts @@ -0,0 +1,113 @@ +import { Request, Response } from 'express'; +import { responseSuccess, responseError } from '../../utils/response.utils'; +import { getRepository } from 'typeorm'; +import { NotificationItem } from '../../entities/NotificationItem'; +import { Notification } from '../../entities/Notification'; +import { getIO } from '../../utils/socket'; +import { getNotifications } from '../../utils/getNotifications'; + +export const updateNotificationsService = async (req: Request, res: Response) => { + try { + let notificationIds: string[] = req.body.notificationIds; + notificationIds = Array.from(new Set(notificationIds)); + + if (!notificationIds.length) { + return responseError(res, 400, 'Please provide Notification IDs to update'); + } + + const notificationRepo = getRepository(Notification); + const notificationItemRepo = getRepository(NotificationItem); + + const notificationItems: string[] = []; + + for (const id of notificationIds) { + try { + const notificationItem = await notificationItemRepo.findOne({ + where: { + id: id, + notification: { + user: { + id: req.user?.id + } + } + } + }); + + if (notificationItem) { + notificationItem.isRead = true; + await notificationItemRepo.save(notificationItem); + notificationItems.push(id); + } + } catch (error) { + continue; + } + } + + const notification = await notificationRepo + .findOne({ + where: { user: { id: req.user?.id } }, + relations: ['allNotifications', 'user'] + }); + + if (notification) { + notification.updateUnread(); + await notificationRepo.save(notification); + } + + getIO().emit('notification', { + action: `${req.user?.email} notification`, + notifications: await getNotifications(req.user!.id!) + }); + + return responseSuccess(res, 200, `${notificationItems.length} of ${notificationIds.length} Notification(s) was successfully updated.`); + + } catch (error) { + return responseError(res, 500, (error as Error).message); + } +}; + +export const updateAllNotificationsService = async (req: Request, res: Response) => { + try { + + const notificationRepo = getRepository(Notification); + const notificationItemRepo = getRepository(NotificationItem); + + const notification = await notificationRepo + .findOne({ + where: { + user: { + id: req.user?.id + }, + allNotifications: { + isRead: false + } + }, + relations: ['allNotifications'] + }); + + if (!notification || !notification.allNotifications.length) { + responseSuccess(res, 200, "User doesn't have any unread notifications."); + return; + } + + for (const notificationItem of notification.allNotifications) { + notificationItem.isRead = true; + await notificationItemRepo.save(notificationItem); + } + + if (notification) { + notification.updateUnread(); + await notificationRepo.save(notification); + } + + getIO().emit('notification', { + action: `${req.user?.email} notification`, + notifications: await getNotifications(req.user!.id!) + }); + + return responseSuccess(res, 200, `All your unread notifications was successfully updated as read.`, notification); + + } catch (error) { + return responseError(res, 500, (error as Error).message); + } +}; \ No newline at end of file diff --git a/src/services/orderServices/createOrder.ts b/src/services/orderServices/createOrder.ts index 038d796..6bec977 100644 --- a/src/services/orderServices/createOrder.ts +++ b/src/services/orderServices/createOrder.ts @@ -8,6 +8,10 @@ import { Cart } from '../../entities/Cart'; import { Transaction } from '../../entities/transaction'; import { responseError, sendErrorResponse, sendSuccessResponse } from '../../utils/response.utils'; import sendMail from '../../utils/sendOrderMail'; +import { VendorOrders } from '../../entities/vendorOrders'; +import { CartItem } from '../../entities/CartItem'; +import { VendorOrderItem } from '../../entities/VendorOrderItem'; +import { sendNotification } from '../../utils/sendNotification'; export const createOrderService = async (req: Request, res: Response) => { const { cartId, address } = req.body; @@ -56,15 +60,6 @@ export const createOrderService = async (req: Request, res: Response) => { orderItem.quantity = item.quantity; orderItems.push(orderItem); } - - if (!buyer.accountBalance || buyer.accountBalance < totalPrice) { - return sendErrorResponse(res, 400, 'Not enough funds to perform this transaction'); - } - - const previousBalance = buyer.accountBalance; - buyer.accountBalance -= totalPrice; - const currentBalance = buyer.accountBalance; - const newOrder = new Order(); newOrder.buyer = buyer; newOrder.totalPrice = totalPrice; @@ -76,7 +71,6 @@ export const createOrderService = async (req: Request, res: Response) => { await getManager().transaction(async transactionalEntityManager => { for (const item of cart.items) { const product = item.product; - product.quantity -= item.quantity; await transactionalEntityManager.save(Product, product); } @@ -92,8 +86,6 @@ export const createOrderService = async (req: Request, res: Response) => { orderTransaction.user = buyer; orderTransaction.order = newOrder; orderTransaction.amount = totalPrice; - orderTransaction.previousBalance = previousBalance; - orderTransaction.currentBalance = currentBalance; orderTransaction.type = 'debit'; orderTransaction.description = 'Purchase of products'; await transactionalEntityManager.save(Transaction, orderTransaction); @@ -103,6 +95,7 @@ export const createOrderService = async (req: Request, res: Response) => { }); const orderResponse = { + id: newOrder.id, fullName: `${newOrder.buyer.firstName} ${newOrder.buyer.lastName}`, email: newOrder.buyer.email, products: orderItems.map(item => ({ @@ -118,14 +111,79 @@ export const createOrderService = async (req: Request, res: Response) => { const message = { subject: 'Order created successfully', - ...orderResponse + ...orderResponse, }; await sendMail(message); + // separate order by each vendor getting order related to his products + await saveVendorRelatedOrder(newOrder, cart.items); + return sendSuccessResponse(res, 201, 'Order created successfully', orderResponse); } catch (error) { - console.error('Error creating order:', error); return sendErrorResponse(res, 500, (error as Error).message); } +}; + +const saveVendorRelatedOrder = async (order: Order, CartItem: CartItem[]) => { + try { + for (const item of CartItem) { + const productRepository = getRepository(Product); + let sendNotif: boolean = false + + const product = await productRepository.findOne({ + where: { + id: item.product.id, + }, + relations: ['vendor'], + }); + + if (!product) return; + + const orderItem = new VendorOrderItem(); + orderItem.product = product; + orderItem['price/unit'] = product.newPrice; + orderItem.quantity = item.quantity; + + const vendorOrdersRepository = getRepository(VendorOrders); + let vendorOrders = await vendorOrdersRepository.findOne({ + where: { + vendor: { + id: product.vendor.id, + }, + order: { + id: order.id, + }, + }, + relations: ['vendorOrderItems'], + }); + + if (vendorOrders) { + vendorOrders.totalPrice = Number(vendorOrders.totalPrice) + +product.newPrice * +item.quantity; + vendorOrders.vendorOrderItems = [...vendorOrders.vendorOrderItems, orderItem]; + } else { + const newVendorOrders = new VendorOrders(); + newVendorOrders.vendor = product.vendor; + newVendorOrders.vendorOrderItems = [orderItem]; + newVendorOrders.order = order; + newVendorOrders.totalPrice = product.newPrice * item.quantity; + vendorOrders = newVendorOrders; + + sendNotif = true; + } + + await vendorOrdersRepository.save(vendorOrders); + + if (sendNotif) { + await sendNotification({ + content: `Buyer "${vendorOrders.order.buyer.firstName} ${vendorOrders.order.buyer.lastName}" has added one of your products to their order. Please confirm that you'll be able to deliver it.`, + type: 'order', + user: vendorOrders.vendor, + link: `/product/vendor/orders/${vendorOrders.id}` + }); + } + } + } catch (error) { + console.log((error as Error).message); + } }; \ No newline at end of file diff --git a/src/services/orderServices/getOrderService.ts b/src/services/orderServices/getOrderService.ts index 18e0664..4006478 100644 --- a/src/services/orderServices/getOrderService.ts +++ b/src/services/orderServices/getOrderService.ts @@ -4,62 +4,116 @@ import { responseSuccess, responseError } from '../../utils/response.utils'; import { Order } from '../../entities/Order'; import { OrderItem } from '../../entities/OrderItem'; - // Example usage: - export const getOrdersService = async (req: Request, res: Response) => { - try { - const orderRepository = getRepository(Order); - const buyerId = req.user?.id; + try { + const orderRepository = getRepository(Order); + const buyerId = req.user?.id; + + const orders = await orderRepository.find({ + where: { + buyer: { + id: buyerId, + }, + }, + relations: ['buyer', 'orderItems', 'orderItems.product'], + order: { + createdAt: 'DESC', // Order by creation date, most recent first + }, + }); + + if (!orders || orders.length === 0) { + return responseSuccess(res, 404, `You haven't made any orders yet`, { orders: [] }); + } + + const sanitezedResponse = orders.map(order => ({ + id: order.id, + totalPrice: order.totalPrice, + orderStatus: order.orderStatus, + quantity: order.quantity, + address: order.address, + orderDate: order.orderDate, + createdAt: order.createdAt, + updatedAt: order.updatedAt, + buyer: { + id: order.buyer.id, + firstName: order.buyer.firstName, + lastName: order.buyer.lastName, + accountBalance: order.buyer.accountBalance, + }, + orderItems: order.orderItems.map((item: OrderItem) => ({ + id: item.id, + price: item.price, + quantity: item.quantity, + product: { + id: item.product.id, + name: item.product.name, + description: item.product.description, + images: item.product.images, + price: item.product.newPrice, + expirationDate: item.product.expirationDate, + }, + })), + })); + responseSuccess(res, 200, 'Orders retrieved successfully', { orders: sanitezedResponse }); + } catch (error) { + return responseError(res, 400, (error as Error).message); + } +}; - const orders = await orderRepository.find({ - where: { - buyer: { - id: buyerId, - } - }, - relations: ['buyer', 'orderItems', 'orderItems.product'], - order: { - createdAt: 'DESC', // Order by creation date, most recent first - }, - }); +export const getOrderService = async (req: Request, res: Response) => { + try { + const orderId = req.params.orderId; + const orderRepository = getRepository(Order); + const buyerId = req.user?.id; - if (!orders || orders.length === 0) { - return responseSuccess(res, 404, `You haven't made any orders yet`, { orders: [] }); - } + const order = await orderRepository.findOne({ + where: { + id: orderId, + buyer: { + id: buyerId, + }, + }, + relations: ['buyer', 'orderItems', 'orderItems.product'] + }); - const sanitezedResponse = orders.map(order => ({ - id: order.id, - totalPrice: order.totalPrice, - orderStatus: order.orderStatus, - quantity: order.quantity, - address: order.address, - orderDate: order.orderDate, - createdAt: order.createdAt, - updatedAt: order.updatedAt, - buyer: { - id: order.buyer.id, - firstName: order.buyer.firstName, - lastName: order.buyer.lastName, - accountBalance: order.buyer.accountBalance - }, - orderItems: order.orderItems.map((item: OrderItem) => ({ - id: item.id, - price: item.price, - quantity: item.quantity, - product: { - id: item.product.id, - name: item.product.name, - description: item.product.description, - images: item.product.images, - price: item.product.newPrice, - expirationDate: item.product.expirationDate, - } - })) - })); - responseSuccess(res, 200, 'Orders retrieved successfully', { orders: sanitezedResponse }); - } catch (error) { - return responseError(res, 400, (error as Error).message); + if (!order) { + return responseSuccess(res, 404, `Order not found.`); } -}; \ No newline at end of file + + const sanitizedResponse = { + id: order.id, + totalPrice: order.totalPrice, + orderStatus: order.orderStatus, + quantity: order.quantity, + address: order.address, + orderDate: order.orderDate, + createdAt: order.createdAt, + updatedAt: order.updatedAt, + buyer: { + id: order.buyer.id, + firstName: order.buyer.firstName, + lastName: order.buyer.lastName, + accountBalance: order.buyer.accountBalance, + }, + orderItems: order.orderItems.map((item: OrderItem) => ({ + id: item.id, + price: item.price, + quantity: item.quantity, + product: { + id: item.product.id, + name: item.product.name, + description: item.product.description, + images: item.product.images, + price: item.product.newPrice, + expirationDate: item.product.expirationDate, + }, + })), + }; + + responseSuccess(res, 200, 'Order retrieved successfully', { order: sanitizedResponse }); + } catch (error) { + return responseError(res, 400, (error as Error).message); + } +}; diff --git a/src/services/orderServices/getOrderTransactionHistory.ts b/src/services/orderServices/getOrderTransactionHistory.ts index 74ae473..6bd0b17 100644 --- a/src/services/orderServices/getOrderTransactionHistory.ts +++ b/src/services/orderServices/getOrderTransactionHistory.ts @@ -23,8 +23,6 @@ export const getTransactionHistoryService = async (req: Request, res: Response) id: transaction.id, amount: transaction.amount, type: transaction.type, - previousBalance: transaction.previousBalance, - currentBalance: transaction.currentBalance, description: transaction.description, createdAt: transaction.createdAt, order: transaction.order diff --git a/src/services/orderServices/updateOrderService.ts b/src/services/orderServices/updateOrderService.ts index f29b47c..d10f5d3 100644 --- a/src/services/orderServices/updateOrderService.ts +++ b/src/services/orderServices/updateOrderService.ts @@ -1,5 +1,5 @@ import { Request, Response } from 'express'; -import { getManager, EntityManager, Repository } from 'typeorm'; +import { getManager, EntityManager, Repository, getRepository } from 'typeorm'; import { Order } from '../../entities/Order'; import { Product } from '../../entities/Product'; import { User } from '../../entities/User'; @@ -7,123 +7,164 @@ import { OrderItem } from '../../entities/OrderItem'; import { Transaction } from '../../entities/transaction'; import { responseError, sendErrorResponse, sendSuccessResponse } from '../../utils/response.utils'; import sendMail from '../../utils/sendOrderMail'; +import { sendNotification } from '../../utils/sendNotification'; +import { VendorOrders } from '../../entities/vendorOrders'; interface OrderStatusType { - orderStatus: 'order placed' | 'cancelled' | 'awaiting shipment' | 'in transit' | 'delivered' | 'received' | 'returned'; + orderStatus: + | 'order placed' + | 'cancelled' + | 'awaiting shipment' + | 'in transit' + | 'delivered' + | 'received' + | 'returned'; } export const updateOrderService = async (req: Request, res: Response) => { - const { orderId } = req.params; - const { orderStatus } = req.body; - - try { - await getManager().transaction(async (transactionalEntityManager: EntityManager) => { - const orderRepository: Repository = transactionalEntityManager.getRepository(Order); - const productRepository: Repository = transactionalEntityManager.getRepository(Product); - const userRepository: Repository = transactionalEntityManager.getRepository(User); - const orderItemRepository: Repository = transactionalEntityManager.getRepository(OrderItem); - const transactionRepository: Repository = transactionalEntityManager.getRepository(Transaction); - - const buyerId = req.user?.id; - if (!buyerId) { - throw new Error('Unauthorized'); + const { orderId } = req.params; + const { orderStatus } = req.body; + + try { + await getManager().transaction(async (transactionalEntityManager: EntityManager) => { + const orderRepository: Repository = transactionalEntityManager.getRepository(Order); + const productRepository: Repository = transactionalEntityManager.getRepository(Product); + const userRepository: Repository = transactionalEntityManager.getRepository(User); + const orderItemRepository: Repository = transactionalEntityManager.getRepository(OrderItem); + const transactionRepository: Repository = transactionalEntityManager.getRepository(Transaction); + + const buyerId = req.user?.id; + if (!buyerId) { + throw new Error('Unauthorized'); + } + + // Fetch order and related entities + const order: Order | null = await orderRepository.findOne({ + where: { id: orderId, buyer: { id: buyerId } }, + relations: ['orderItems', 'orderItems.product', 'buyer'], + }); + + if (!order) { + return sendErrorResponse(res, 404, 'Order not found'); + } + // Check if order can be updated + if (isOrderFinalStatus(order.orderStatus)) { + return sendErrorResponse(res, 401, `Order cannot be updated once it is ${order.orderStatus}`); + } + + // Handle order status transitions + if (orderStatus !== undefined && order.orderStatus !== orderStatus) { + switch (orderStatus) { + case 'cancelled': + case 'returned': + if (order.orderStatus !== 'delivered') { + await processRefund(order, transactionalEntityManager); } - - // Fetch order and related entities - const order: Order | null = await orderRepository.findOne({ - where: { id: orderId, buyer: { id: buyerId } }, - relations: ['orderItems', 'orderItems.product', 'buyer'], - }); - - if (!order) { - return sendErrorResponse(res, 404, "Order not found"); - } - // Check if order can be updated - if (isOrderFinalStatus(order.orderStatus)) { - return sendErrorResponse(res, 401, `Order cannot be updated once it is ${order.orderStatus}`); - } - - // Handle order status transitions - if (orderStatus !== undefined && order.orderStatus !== orderStatus) { - switch (orderStatus) { - case 'cancelled': - case 'returned': - if (order.orderStatus !== 'delivered') { - await processRefund(order, transactionalEntityManager); - } - break; - default: - break; - } - - order.orderStatus = orderStatus; - } - - // Save updated order status - await orderRepository.save(order); - - // Prepare response data - const orderResponse = { - fullName: `${order.buyer.firstName} ${order.buyer.lastName}`, - email: order.buyer.email, - products: order.orderItems.map((item: OrderItem) => ({ - name: item.product.name, - newPrice: item.price, - quantity: item.quantity, - })), - totalAmount: order.totalPrice, - quantity: order.quantity, - orderDate: order.orderDate, - address: order.address, - }; - - // Send email notification - const message = { - subject: 'Order updated successfully', - ...orderResponse - }; - await sendMail(message); - - // Respond with success - return sendSuccessResponse(res, 200, 'Order updated successfully', orderResponse); + break; + default: + break; + } + + order.orderStatus = orderStatus; + } + + // Save updated order status + await orderRepository.save(order); + + if (orderStatus === 'received') { + const admins = await getRepository(User).find({ + where: { + role: 'ADMIN' + } + }); + + admins.forEach( async (admin) => { + await sendNotification({ + content: `The Buyer named "${order.buyer.firstName} ${order.buyer.lastName}", has confirmed that they have successfully received their order.`, + type: 'order', + user: admin, + link: `/product/admin/orders/${order.id}` + }); }); - } catch (error) { - console.error('Error updating order:', error); - return sendErrorResponse(res, 500, (error as Error).message); - } + } + + const vendorOrders = await getRepository(VendorOrders).find({ + where: { + order: { + id: order.id + } + }, + relations: { + vendor: true + } + }); + + vendorOrders.forEach(async (vendorOrder) => { + await sendNotification({ + content: `The Buyer named "${order.buyer.firstName} ${order.buyer.lastName}", has marked their order as "${orderStatus}". Please ensure that you update the order status on your side as well.`, + type: 'order', + user: vendorOrder.vendor, + link: `/product/vendor/orders/${vendorOrder.id}` + }); + }); + + // Prepare response data + const orderResponse = { + fullName: `${order.buyer.firstName} ${order.buyer.lastName}`, + email: order.buyer.email, + products: order.orderItems.map((item: OrderItem) => ({ + name: item.product.name, + newPrice: item.price, + quantity: item.quantity, + })), + totalAmount: order.totalPrice, + quantity: order.quantity, + orderDate: order.orderDate, + address: order.address, + }; + + // Send email notification + const message = { + subject: 'Order updated successfully', + ...orderResponse, + }; + await sendMail(message); + + // Respond with success + return sendSuccessResponse(res, 200, 'Order updated successfully', orderResponse); + }); + } catch (error) { + console.error('Error updating order:', (error as Error).message); + return sendErrorResponse(res, 500, (error as Error).message); + } }; -async function processRefund(order: Order, entityManager: EntityManager) { - const buyer = order.buyer; - - // Refund buyer - const previousBalance = buyer.accountBalance; - buyer.accountBalance += order.totalPrice; - const currentBalance = buyer.accountBalance; - await entityManager.save(buyer); - - // Record refund transaction - const refundTransaction = new Transaction(); - refundTransaction.user = buyer; - refundTransaction.order = order; - refundTransaction.amount = order.totalPrice; - refundTransaction.previousBalance = previousBalance; - refundTransaction.currentBalance = currentBalance; - refundTransaction.type = 'credit'; - refundTransaction.description = 'Refund for cancelled or returned order'; - await entityManager.save(refundTransaction); - - // Return products to store - for (const orderItem of order.orderItems) { - const product = orderItem.product; - product.quantity += orderItem.quantity; - await entityManager.save(product); - } - - // Clear order details - order.orderItems = []; - order.totalPrice = 0; - order.quantity = 0; +async function processRefund (order: Order, entityManager: EntityManager) { + const buyer = order.buyer; + + // Refund buyer + await entityManager.save(buyer); + + // Record refund transaction + const refundTransaction = new Transaction(); + refundTransaction.user = buyer; + refundTransaction.order = order; + refundTransaction.amount = order.totalPrice; + refundTransaction.type = 'credit'; + refundTransaction.description = 'Refund for cancelled or returned order'; + await entityManager.save(refundTransaction); + + // Return products to store + for (const orderItem of order.orderItems) { + const product = orderItem.product; + product.quantity += orderItem.quantity; + await entityManager.save(product); + } + + // Clear order details + order.orderItems = []; + order.totalPrice = 0; + order.quantity = 0; } -function isOrderFinalStatus(status: string): boolean { - return ['cancelled', 'delivered', 'returned'].includes(status); -} \ No newline at end of file +function isOrderFinalStatus (status: string): boolean { + return ['cancelled', 'delivered', 'returned', 'completed'].includes(status); +} diff --git a/src/services/productServices/deleteProduct.ts b/src/services/productServices/deleteProduct.ts index 43ec3d1..068c4c9 100644 --- a/src/services/productServices/deleteProduct.ts +++ b/src/services/productServices/deleteProduct.ts @@ -3,30 +3,28 @@ import { Product } from '../../entities/Product'; import { getRepository } from 'typeorm'; import { responseError, responseSuccess } from '../../utils/response.utils'; - export const deleteProductService = async (req: Request, res: Response) => { - try { - const { id } = req.params; - - const productRepository = getRepository(Product); + try { + const { id } = req.params; - const product = await productRepository.findOne({ - where: { - id: id, - vendor: { - id: req.user?.id - } - } - }); + const productRepository = getRepository(Product); - if (product) { - await productRepository.remove(product); - return responseSuccess(res, 200, 'Product successfully deleted'); - } + const product = await productRepository.findOne({ + where: { + id: id, + vendor: { + id: req.user?.id, + }, + }, + }); - return responseError(res, 404, 'Product not found'); - - } catch (error) { - responseError(res, 400, (error as Error).message); + if (product) { + await productRepository.remove(product); + return responseSuccess(res, 200, 'Product successfully deleted'); } + + return responseError(res, 404, 'Product not found'); + } catch (error) { + responseError(res, 400, (error as Error).message); + } }; diff --git a/src/services/productServices/getRecommendedProductsService.ts b/src/services/productServices/getRecommendedProductsService.ts index 19368e1..533dcd9 100644 --- a/src/services/productServices/getRecommendedProductsService.ts +++ b/src/services/productServices/getRecommendedProductsService.ts @@ -1,62 +1,64 @@ -import { Request, Response } from "express"; -import { responseError, responseSuccess } from "../../utils/response.utils"; -import { getRepository } from "typeorm"; -import { Product } from "../../entities/Product"; +import { Request, Response } from 'express'; +import { responseError, responseSuccess } from '../../utils/response.utils'; +import { getRepository } from 'typeorm'; +import { Product } from '../../entities/Product'; interface conditionDoc { - categories: any[] | null; - vendor: any | null + categories: any[] | null; + vendor: any | null; } export const getRecommendedProductsService = async (req: Request, res: Response) => { + try { + // Define pagination parameters + const page = req.query.page ? Number(req.query.page) : 1; + const limit = req.query.limit ? Number(req.query.limit) : 10; + const skip = (page - 1) * limit; + const condition: conditionDoc = { + categories: null, + vendor: null, + }; - try { - // Define pagination parameters - const page = req.query.page ? Number(req.query.page) : 1; - const limit = req.query.limit ? Number(req.query.limit) : 10; - const skip = (page - 1) * limit; - const condition: conditionDoc = { - categories: null, - vendor: null - }; - - if (req.query.categories) { - const categoryIds = Array.isArray(req.query.categories) ? req.query.categories : [req.query.categories]; - condition.categories = categoryIds; - }; - if (req.query.vendor) condition.vendor = req.query.vendor; + if (req.query.categories) { + const categoryIds = Array.isArray(req.query.categories) ? req.query.categories : [req.query.categories]; + condition.categories = categoryIds; + } + if (req.query.vendor) condition.vendor = req.query.vendor; - const productRepository = getRepository(Product); - const productsQuery = productRepository.createQueryBuilder("product") - .leftJoinAndSelect("product.categories", "category") - .leftJoinAndSelect("product.vendor", "vendor") - .where("1 = 1"); + const productRepository = getRepository(Product); + const productsQuery = productRepository + .createQueryBuilder('product') + .leftJoinAndSelect('product.categories', 'category') + .leftJoinAndSelect('product.vendor', 'vendor') + .leftJoinAndSelect('product.feedbacks', 'feedbacks') + .where('1 = 1'); - if (condition.categories && condition.categories.length > 0) { - productsQuery.andWhere("category.id IN (:...categories)", { categories: condition.categories }); - } - if (condition.vendor) { - productsQuery.andWhere("vendor.id = :vendorId", { vendorId: condition.vendor }); - } + if (condition.categories && condition.categories.length > 0) { + productsQuery.andWhere('category.id IN (:...categories)', { categories: condition.categories }); + } + if (condition.vendor) { + productsQuery.andWhere('vendor.id = :vendorId', { vendorId: condition.vendor }); + } - const products = await productsQuery - .skip(skip) - .take(limit) - .getMany(); - if (products.length < 1) { - return responseSuccess(res, 200, `No products found for the specified ${condition.vendor ? 'vendor' : 'category'}`); - } - const sanitizedProducts = products.map(product => ({ - ...product, - vendor: { - firstName: product.vendor.firstName, - lastName: product.vendor.lastName, - phoneNumber: product.vendor.phoneNumber, - photoUrl: product.vendor.photoUrl - } - })); - return responseSuccess(res, 200, 'Products retrieved', { products: sanitizedProducts }); - } catch (error) { - return responseError(res, 400, (error as Error).message); + const products = await productsQuery.skip(skip).take(limit).getMany(); + if (products.length < 1) { + return responseSuccess( + res, + 200, + `No products found for the specified ${condition.vendor ? 'vendor' : 'category'}` + ); } -}; \ No newline at end of file + const sanitizedProducts = products.map(product => ({ + ...product, + vendor: { + firstName: product.vendor.firstName, + lastName: product.vendor.lastName, + phoneNumber: product.vendor.phoneNumber, + photoUrl: product.vendor.photoUrl, + }, + })); + return responseSuccess(res, 200, 'Products retrieved', { products: sanitizedProducts }); + } catch (error) { + return responseError(res, 400, (error as Error).message); + } +}; diff --git a/src/services/productServices/listAllProductsService.ts b/src/services/productServices/listAllProductsService.ts index 8950abd..4429e89 100644 --- a/src/services/productServices/listAllProductsService.ts +++ b/src/services/productServices/listAllProductsService.ts @@ -5,38 +5,40 @@ import { responseError, responseSuccess } from '../../utils/response.utils'; import { validate } from 'uuid'; export const listAllProductsService = async (req: Request, res: Response) => { - try { - const page = req.query.page ? Number(req.query.page) : 1; - const limit = req.query.limit ? Number(req.query.limit) : 10; - const skip = (page - 1) * limit; - const category = req.query.category ; + try { + const page = req.query.page ? Number(req.query.page) : 1; + const limit = req.query.limit ? Number(req.query.limit) : 10; + const skip = (page - 1) * limit; + const category = req.query.category; - - const productRepository = getRepository(Product); - const products = await productRepository.find({ - where: { - categories: { - name: category as string - } - }, - skip, - take: limit, - relations: ["categories","vendor"], - select: { - vendor: { - id: true, firstName: true, lastName: true, - email: true, phoneNumber: true, photoUrl: true - } - } - } - ); + const productRepository = getRepository(Product); + const products = await productRepository.find({ + where: { + categories: { + name: category as string, + }, + }, + skip, + take: limit, + relations: ['categories', 'vendor', 'feedbacks'], + select: { + vendor: { + id: true, + firstName: true, + lastName: true, + email: true, + phoneNumber: true, + photoUrl: true, + }, + }, + }); - if (products.length < 1) { - return responseSuccess(res, 200, 'No products found'); - } - - return responseSuccess(res, 200, 'Products retrieved', { products }); - } catch (error) { - responseError(res, 400, (error as Error).message); + if (products.length < 1) { + return responseSuccess(res, 200, 'No products found'); } + + return responseSuccess(res, 200, 'Products retrieved', { products }); + } catch (error) { + responseError(res, 400, (error as Error).message); + } }; diff --git a/src/services/productServices/payment.ts b/src/services/productServices/payment.ts new file mode 100644 index 0000000..b613296 --- /dev/null +++ b/src/services/productServices/payment.ts @@ -0,0 +1,52 @@ +import { Request, Response } from 'express'; +import { Cart } from '../../entities/Cart'; // Import your Cart entity +import { Order } from '../../entities/Order'; // Import your Order entity +import { getRepository, getTreeRepository } from 'typeorm'; +import dotenv from 'dotenv'; +import Stripe from 'stripe'; +dotenv.config(); +const stripeInstance = new Stripe(process.env.STRIPE_SECRET_KEY as string, { + apiVersion: "2024-04-10", +}); + +export const confirmPayment = async (req: Request, res: Response) => { + try { + const { payment_method } = req.body; + const cartId = req.params.cartId; // Get the cart ID from the params + + const cartRepository = getRepository(Cart); + const orderRepository = getTreeRepository(Order) + const cart = await cartRepository.findOne({where: {id : cartId}}); + if (!cart) { + return res.status(404).json({ error: 'Cart not found.' }); + } + const order = await orderRepository.findOne({ where: { buyer: cart.user } }); + if (!order) { + return res.status(404).json({ error: 'order not found.' }); + } + + const paymentIntent = await stripeInstance.paymentIntents.create({ + amount: cart.totalAmount, // Convert total to cents + currency: 'usd', + description: `Order #${cartId}`, + return_url: 'https://frontend-website.com/success', + confirm: true, + payment_method, + }); + + order.orderStatus = 'awaiting shipment'; + await orderRepository.save(order); + + + if (paymentIntent.status === 'succeeded') { + // Payment succeeded + res.status(200).json({ message: 'Payment successful!' }); + } else { + // Payment failed + res.status(400).json({ error: 'Payment failed.' }); + } + } catch (error) { + console.error('Error confirming payment:', error); + res.status(500).json({ error: 'Something went wrong' }); + } +}; \ No newline at end of file diff --git a/src/services/productServices/readProduct.ts b/src/services/productServices/readProduct.ts index 5c9257c..2836b21 100644 --- a/src/services/productServices/readProduct.ts +++ b/src/services/productServices/readProduct.ts @@ -4,67 +4,75 @@ import { getRepository } from 'typeorm'; import { responseError, responseSuccess } from '../../utils/response.utils'; export const readProductsService = async (req: Request, res: Response) => { - try { - // Define pagination parameters - const page = req.query.page ? Number(req.query.page) : 1; - const limit = req.query.limit ? Number(req.query.limit) : 10; - const skip = (page - 1) * limit; + try { + // Define pagination parameters + const page = req.query.page ? Number(req.query.page) : 1; + const limit = req.query.limit ? Number(req.query.limit) : 10; + const skip = (page - 1) * limit; - // Retrieve products - const productRepository = getRepository(Product); - const products = await productRepository.find({ - where: { - vendor: { - id: req.user?.id, - }, - }, - skip, - take: limit, - relations: ['categories', 'vendor'], - select: { - vendor: { - id: true, firstName: true, lastName: true, - email: true, phoneNumber: true, photoUrl: true - } - } - }); + // Retrieve products + const productRepository = getRepository(Product); + const products = await productRepository.find({ + where: { + vendor: { + id: req.user?.id, + }, + }, + skip, + take: limit, + relations: ['categories', 'vendor', 'feedbacks'], + select: { + vendor: { + id: true, + firstName: true, + lastName: true, + email: true, + phoneNumber: true, + photoUrl: true, + }, + }, + }); - if (products.length < 1) { - return responseSuccess(res, 200, 'You have no products yet'); - } - return responseSuccess(res, 200, 'Products retrieved', { products }); - } catch (error) { - responseError(res, 400, (error as Error).message); + if (products.length < 1) { + return responseSuccess(res, 200, 'You have no products yet'); } + return responseSuccess(res, 200, 'Products retrieved', { products }); + } catch (error) { + responseError(res, 400, (error as Error).message); + } }; export const readProductService = async (req: Request, res: Response) => { - try { - const { id } = req.params; + try { + const { id } = req.params; - const productRepository = getRepository(Product); - const product = await productRepository.findOne({ - where: { - id: id, - vendor: { - id: req.user?.id, - }, - }, - relations: ['categories', 'vendor'], - select: { - vendor: { - id: true, firstName: true, lastName: true, - email: true, phoneNumber: true, photoUrl: true - } - } - }); + const productRepository = getRepository(Product); + const product = await productRepository.findOne({ + where: { + id: id, + vendor: { + id: req.user?.id, + }, + }, + relations: ['categories', 'vendor'], + select: { + vendor: { + id: true, + firstName: true, + lastName: true, + email: true, + phoneNumber: true, + photoUrl: true, + }, + }, + }); - if (!product) { - return responseError(res, 404, 'Product not found'); - } - - return responseSuccess(res, 200, 'Product retrieved', { product }); - } catch (error) { - responseError(res, 400, (error as Error).message); + if (!product) { + return responseError(res, 404, 'Product not found'); } + + return responseSuccess(res, 200, 'Product retrieved', { product }); + } catch (error) { + responseError(res, 400, (error as Error).message); + } }; diff --git a/src/services/productServices/removeProductImage.ts b/src/services/productServices/removeProductImage.ts index 2995593..4424676 100644 --- a/src/services/productServices/removeProductImage.ts +++ b/src/services/productServices/removeProductImage.ts @@ -21,7 +21,7 @@ export const removeProductImageService = async (req: Request, res: Response) => const product = await productRepository.findOne({ where: { id, - vendor: { id: req.user?.id } + vendor: { id: req.user?.id }, }, relations: ['vendor'], }); diff --git a/src/services/productServices/searchProduct.ts b/src/services/productServices/searchProduct.ts index 765f431..9f33b5f 100644 --- a/src/services/productServices/searchProduct.ts +++ b/src/services/productServices/searchProduct.ts @@ -1,4 +1,4 @@ -import { Request, Response } from "express"; +import { Request, Response } from 'express'; import { getRepository, Like } from 'typeorm'; import { Product } from '../../entities/Product'; @@ -26,10 +26,7 @@ export const searchProductService = async (params: SearchProductParams) => { const skip = (page - 1) * limit; - const [products, total] = await query - .skip(skip) - .take(limit) - .getManyAndCount(); + const [products, total] = await query.skip(skip).take(limit).getManyAndCount(); const totalPages = Math.ceil(total / limit); diff --git a/src/services/productServices/viewSingleProduct.ts b/src/services/productServices/viewSingleProduct.ts index f956625..be9764d 100644 --- a/src/services/productServices/viewSingleProduct.ts +++ b/src/services/productServices/viewSingleProduct.ts @@ -4,35 +4,28 @@ import { getRepository } from 'typeorm'; import { responseError } from '../../utils/response.utils'; import { validate } from 'uuid'; - - export const viewSingleProduct = async (req: Request, res: Response) => { - try { - const productId = req.params.id; + try { + const productId = req.params.id; + + if (!validate(productId)) { + return res.status(400).json({ status: 'error', message: 'Invalid product ID' }); + } + if (productId) { + const products = getRepository(Product); + const product = await products.findOne({ where: { id: productId }, relations: ['categories', 'vendor', 'feedbacks'], }); - if (!validate(productId)) { - return res.status(400).json({ status: 'error', message: 'Invalid product ID' }); - + if (!product) { + return res.status(404).send({ status: 'error', message: 'Product not found' }); } - if(productId){ - const products = getRepository(Product); - const product = await products.findOneBy({ id: productId }); - - if (!product) { - return res.status(404).send({status:'error', message: 'Product not found'}); - - } - - if (product.expirationDate && new Date(product.expirationDate) < new Date()) { - return res.status(400).json({ status: 'error', message: 'Product expired' }); - - } - res.status(200).json({ status: 'success', product: product }); + if (product.expirationDate && new Date(product.expirationDate) < new Date()) { + return res.status(400).json({ status: 'error', message: 'Product expired' }); } - - } catch (error) { - console.error('Error handling request:', error); - res.status(500).send('Error fetching product details'); + res.status(200).json({ status: 'success', product: product }); } -} \ No newline at end of file + } catch (error) { + console.error('Error handling request:', error); + res.status(500).send('Error fetching product details'); + } +}; diff --git a/src/services/userServices/userDisableTwoFactorAuth.ts b/src/services/userServices/userDisableTwoFactorAuth.ts index 63729fd..b4fc6e9 100644 --- a/src/services/userServices/userDisableTwoFactorAuth.ts +++ b/src/services/userServices/userDisableTwoFactorAuth.ts @@ -1,6 +1,7 @@ import { Request, Response } from 'express'; import { User } from '../../entities/User'; import { getRepository } from 'typeorm'; +import { sendNotification } from '../../utils/sendNotification'; export const userDisableTwoFactorAuth = async (req: Request, res: Response) => { try { @@ -20,6 +21,11 @@ export const userDisableTwoFactorAuth = async (req: Request, res: Response) => { user.twoFactorEnabled = false; await userRepository.save(user); + await sendNotification({ + content: "You disabled Two factor authentication on you account", + type: 'user', + user: user + }) return res.status(200).json({ status: 'success', message: 'Two factor authentication disabled successfully' }); } catch (error) { if (error instanceof Error) { diff --git a/src/services/userServices/userEnableTwoFactorAuth.ts b/src/services/userServices/userEnableTwoFactorAuth.ts index 16b36be..c5d3cbf 100644 --- a/src/services/userServices/userEnableTwoFactorAuth.ts +++ b/src/services/userServices/userEnableTwoFactorAuth.ts @@ -1,6 +1,7 @@ import { Request, Response } from 'express'; import { User } from '../../entities/User'; import { getRepository } from 'typeorm'; +import { sendNotification } from '../../utils/sendNotification'; export const userEnableTwoFactorAuth = async (req: Request, res: Response) => { try { @@ -20,6 +21,11 @@ export const userEnableTwoFactorAuth = async (req: Request, res: Response) => { user.twoFactorEnabled = true; await userRepository.save(user); + await sendNotification({ + content: "You enabled Two factor authentication on you account", + type: 'user', + user: user + }) return res.status(200).json({ status: 'success', message: 'Two factor authentication enabled successfully' }); } catch (error) { if (error instanceof Error) { diff --git a/src/services/vendorOrderServices/readVendorOrder.ts b/src/services/vendorOrderServices/readVendorOrder.ts new file mode 100644 index 0000000..feec0c3 --- /dev/null +++ b/src/services/vendorOrderServices/readVendorOrder.ts @@ -0,0 +1,119 @@ +import { Request, Response } from 'express'; +import { getRepository } from 'typeorm'; +import { responseSuccess, responseError } from '../../utils/response.utils'; +import { VendorOrderItem } from '../../entities/VendorOrderItem'; +import { VendorOrders } from '../../entities/vendorOrders'; + +export const getVendorOrdersService = async (req: Request, res: Response) => { + try { + const vendorOrderRepository = getRepository(VendorOrders); + const vendorId = req.user?.id; + + const vendorOrders = await vendorOrderRepository.find({ + where: { + vendor: { + id: vendorId, + }, + }, + relations: ['vendor', 'order.buyer', 'vendorOrderItems', 'vendorOrderItems.product'], + order: { + createdAt: 'DESC', // Order by creation date, most recent first + }, + }); + + if (!vendorOrders.length) { + return responseError(res, 200, `You don't have any pending orders from buyer`, { orders: [] }); + } + + const sanitizedOrderResponse = vendorOrders.map(order => ({ + id: order.id, + totalPrice: order.totalPrice, + orderStatus: order.orderStatus, + address: order.order.address, + createdAt: order.createdAt, + updatedAt: order.updatedAt, + vendor: { + id: order.vendor.id, + firstName: order.vendor.firstName, + lastName: order.vendor.lastName, + email: order.vendor.email, + gender: order.vendor.gender, + phoneNumber: order.vendor.phoneNumber, + photoUrl: order.vendor.photoUrl, + }, + buyer: { + id: order.order.buyer.id, + firstName: order.order.buyer.firstName, + lastName: order.order.buyer.lastName, + email: order.order.buyer.lastName, + gender: order.order.buyer.gender, + phoneNumber: order.order.buyer.phoneNumber, + photoUrl: order.order.buyer.photoUrl, + }, + vendorOrderItems: order.vendorOrderItems, + })); + responseSuccess(res, 200, 'Orders retrieved successfully', { + orders: sanitizedOrderResponse, + }); + } catch (error) { + return responseError(res, 400, (error as Error).message); + } +}; + +// Get single vendor order info +export const getSingleVendorOrderService = async (req: Request, res: Response) => { + try { + const vendorOrderId = req.params.id; + + const vendorOrderRepository = getRepository(VendorOrders); + const vendorId = req.user?.id; + + const vendorOrder = await vendorOrderRepository.findOne({ + where: { + id: vendorOrderId, + vendor: { + id: vendorId, + }, + }, + relations: ['vendor', 'order.buyer', 'vendorOrderItems', 'vendorOrderItems.product'], + }); + + if (!vendorOrder) { + return responseError(res, 404, `Order Not Found.`); + } + + const sanitizedOrderResponse = { + id: vendorOrder.id, + totalPrice: vendorOrder.totalPrice, + orderStatus: vendorOrder.orderStatus, + address: vendorOrder.order.address, + createdAt: vendorOrder.createdAt, + updatedAt: vendorOrder.updatedAt, + vendor: { + id: vendorOrder.vendor.id, + firstName: vendorOrder.vendor.firstName, + lastName: vendorOrder.vendor.lastName, + email: vendorOrder.vendor.email, + gender: vendorOrder.vendor.gender, + phoneNumber: vendorOrder.vendor.phoneNumber, + photoUrl: vendorOrder.vendor.photoUrl, + }, + buyer: { + id: vendorOrder.order.buyer.id, + firstName: vendorOrder.order.buyer.firstName, + lastName: vendorOrder.order.buyer.lastName, + email: vendorOrder.order.buyer.lastName, + gender: vendorOrder.order.buyer.gender, + phoneNumber: vendorOrder.order.buyer.phoneNumber, + photoUrl: vendorOrder.order.buyer.photoUrl, + }, + vendorOrderItems: vendorOrder.vendorOrderItems, + }; + + responseSuccess(res, 200, 'Order retrieved successfully', { + order: sanitizedOrderResponse, + }); + } catch (error) { + return responseError(res, 400, (error as Error).message); + } +}; diff --git a/src/services/vendorOrderServices/updateVendorOrder.ts b/src/services/vendorOrderServices/updateVendorOrder.ts new file mode 100644 index 0000000..cae2c60 --- /dev/null +++ b/src/services/vendorOrderServices/updateVendorOrder.ts @@ -0,0 +1,87 @@ +import { Request, Response } from 'express'; +import { getRepository } from 'typeorm'; +import { OrderItem } from '../../entities/OrderItem'; +import { responseError, responseSuccess } from '../../utils/response.utils'; +import sendMail from '../../utils/sendOrderMail'; +import { VendorOrders } from '../../entities/vendorOrders'; +import { getIO } from '../../utils/socket'; + +export const updateVendorOrderService = async (req: Request, res: Response) => { + try { + const vendorOrderId = req.params.id; + const { orderStatus } = req.body; + if ( + !['pending', 'is-accepted', 'in-transit', 'cancelled', 'delivered'].includes( + (orderStatus as string).toLowerCase() + ) + ) { + return responseError(res, 400, `Please provide one of defined statuses.`); + } + + const vendorOrderRepository = getRepository(VendorOrders); + const vendorId = req.user?.id; + + const vendorOrder = await vendorOrderRepository.findOne({ + where: { + id: vendorOrderId, + vendor: { + id: vendorId, + }, + }, + relations: ['vendor', 'order.buyer', 'vendorOrderItems', 'vendorOrderItems.product'], + }); + + if (!vendorOrder) { + return responseError(res, 404, `Order Not Found.`); + } + + // Check if order can be updated + if (['delivered', 'cancelled', 'completed'].includes(vendorOrder.orderStatus)) { + return responseError(res, 409, `Order cannot be updated once it is marked as ${vendorOrder.orderStatus}`); + } + + vendorOrder.orderStatus = (orderStatus as string).toLowerCase(); + + // Save updated order status + const updatedVendorOrder = await vendorOrderRepository.save(vendorOrder); + + const sanitizedOrderResponse = { + id: updatedVendorOrder.id, + totalPrice: updatedVendorOrder.totalPrice, + orderStatus: updatedVendorOrder.orderStatus, + address: updatedVendorOrder.order.address, + createdAt: updatedVendorOrder.createdAt, + updatedAt: updatedVendorOrder.updatedAt, + vendor: { + id: updatedVendorOrder.vendor.id, + firstName: updatedVendorOrder.vendor.firstName, + lastName: updatedVendorOrder.vendor.lastName, + email: updatedVendorOrder.vendor.email, + gender: updatedVendorOrder.vendor.gender, + phoneNumber: updatedVendorOrder.vendor.phoneNumber, + photoUrl: updatedVendorOrder.vendor.photoUrl, + }, + buyer: { + id: updatedVendorOrder.order.buyer.id, + firstName: updatedVendorOrder.order.buyer.firstName, + lastName: updatedVendorOrder.order.buyer.lastName, + email: updatedVendorOrder.order.buyer.lastName, + gender: updatedVendorOrder.order.buyer.gender, + phoneNumber: updatedVendorOrder.order.buyer.phoneNumber, + photoUrl: updatedVendorOrder.order.buyer.photoUrl, + }, + vendorOrderItems: updatedVendorOrder.vendorOrderItems, + }; + + getIO().emit('orders', { + action: 'vendor update', + order: sanitizedOrderResponse, + }); + + return responseSuccess(res, 200, 'Order updated successfully', { + order: sanitizedOrderResponse, + }); + } catch (error) { + return responseError(res, 400, (error as Error).message); + } +}; diff --git a/src/services/wishListServices/addProduct.ts b/src/services/wishListServices/addProduct.ts index da3db89..79d0a38 100644 --- a/src/services/wishListServices/addProduct.ts +++ b/src/services/wishListServices/addProduct.ts @@ -4,57 +4,54 @@ import { getRepository } from 'typeorm'; import { wishList } from '../../entities/wishList'; import { Product } from '../../entities/Product'; -export const addProductService = async (req:Request,res:Response)=>{ - try { +export const addProductService = async (req: Request, res: Response) => { + try { + const id = req.params.id; + const wishListRepository = getRepository(wishList); + const productRepository = getRepository(Product); - const id = req.params.id; - const wishListRepository = getRepository(wishList); - const productRepository = getRepository(Product); + const product = await productRepository.findOne({ where: { id } }); + if (!product) { + return res.status(404).json({ message: 'Product not found' }); + } - const product = await productRepository.findOne({where: { id }}); - - if(!product){ - return res.status(404).json({message: "Product not found"}); - } - - const productDetails = { - productId: product.id, - name: product.name, - image: product.images, - newPrice: product.newPrice, - vendorId: product.vendor - } - - const alreadyIn = await wishListRepository.findOne({where: {productId: id, buyer:{ id: req.user?.id} }}) - - if(alreadyIn){ - return res.status(401).json({ - data: { - message: 'Product Already in the wish list', - wishlistAdded: alreadyIn, - product: productDetails, - }, - }) - } - - const addNewProduct = new wishList(); - addNewProduct.productId = id; - addNewProduct.buyer = req.user as User; - - await wishListRepository.save(addNewProduct); + const productDetails = { + productId: product.id, + name: product.name, + image: product.images, + newPrice: product.newPrice, + vendorId: product.vendor, + }; - addNewProduct.buyer = { id: addNewProduct.buyer.id } as unknown as User; + const alreadyIn = await wishListRepository.findOne({ where: { productId: id, buyer: { id: req.user?.id } } }); - return res.status(201).json({ + if (alreadyIn) { + return res.status(401).json({ data: { - message: 'Product Added to wish list', - wishlistAdded: addNewProduct, + message: 'Product Already in the wish list', + wishlistAdded: alreadyIn, product: productDetails, - }, - }); - - } catch (error) { - return res.status(500).json({ error: 'Internal server error' }); + }, + }); } -} \ No newline at end of file + + const addNewProduct = new wishList(); + addNewProduct.productId = id; + addNewProduct.buyer = req.user as User; + + await wishListRepository.save(addNewProduct); + + addNewProduct.buyer = { id: addNewProduct.buyer.id } as unknown as User; + + return res.status(201).json({ + data: { + message: 'Product Added to wish list', + wishlistAdded: addNewProduct, + product: productDetails, + }, + }); + } catch (error) { + return res.status(500).json({ error: 'Internal server error' }); + } +}; diff --git a/src/services/wishListServices/clearAll.ts b/src/services/wishListServices/clearAll.ts index 88af3c6..7299454 100644 --- a/src/services/wishListServices/clearAll.ts +++ b/src/services/wishListServices/clearAll.ts @@ -2,19 +2,18 @@ import { Request, Response } from 'express'; import { getRepository } from 'typeorm'; import { wishList } from '../../entities/wishList'; -export const clearAllProductService = async (req:Request,res:Response)=>{ - try { - const wishListRepository = getRepository(wishList); - const productsForBuyer = await wishListRepository.find({where: { buyer:{ id: req.user?.id} }}); +export const clearAllProductService = async (req: Request, res: Response) => { + try { + const wishListRepository = getRepository(wishList); + const productsForBuyer = await wishListRepository.find({ where: { buyer: { id: req.user?.id } } }); - if (productsForBuyer.length === 0) { - return res.status(404).json({ message: 'No products in wish list' }); - } - - await wishListRepository.remove(productsForBuyer); - return res.status(200).json({ message: 'All products removed successfully'}); - - } catch (error) { - return res.status(500).json({ error: 'Internal server error' }); + if (productsForBuyer.length === 0) { + return res.status(404).json({ message: 'No products in wish list' }); } -} \ No newline at end of file + + await wishListRepository.remove(productsForBuyer); + return res.status(200).json({ message: 'All products removed successfully' }); + } catch (error) { + return res.status(500).json({ error: 'Internal server error' }); + } +}; diff --git a/src/services/wishListServices/getProducts.ts b/src/services/wishListServices/getProducts.ts index 107f3aa..98dc434 100644 --- a/src/services/wishListServices/getProducts.ts +++ b/src/services/wishListServices/getProducts.ts @@ -3,36 +3,37 @@ import { getRepository } from 'typeorm'; import { wishList } from '../../entities/wishList'; import { Product } from '../../entities/Product'; -export const getProductsService = async (req:Request,res:Response)=>{ - try { - const wishListRepository = getRepository(wishList); - const productRepository =getRepository(Product); +export const getProductsService = async (req: Request, res: Response) => { + try { + const wishListRepository = getRepository(wishList); + const productRepository = getRepository(Product); - const productsForBuyer = await wishListRepository.find({where: { buyer:{ id: req.user?.id} }}); + const productsForBuyer = await wishListRepository.find({ where: { buyer: { id: req.user?.id } } }); - if (productsForBuyer.length === 0) { - return res.status(404).json({ message: 'No products in wish list', products: productsForBuyer }); - } - - const buyerWishProducts = await Promise.all(productsForBuyer.map(async (product) => { - const productDetails = await productRepository.findOne({ where: { id: product.productId } }); - if(productDetails){ - return { - wishListDetails: product, - productInfo: { - productId: productDetails.id, - name: productDetails.name, - image: productDetails.images, - newPrice: productDetails.newPrice, - vendorId: productDetails.vendor - } - }; - } - })); + if (productsForBuyer.length === 0) { + return res.status(404).json({ message: 'No products in wish list', products: productsForBuyer }); + } - return res.status(200).json({ message: 'Products retrieved', productsForBuyer: buyerWishProducts }); + const buyerWishProducts = await Promise.all( + productsForBuyer.map(async product => { + const productDetails = await productRepository.findOne({ where: { id: product.productId } }); + if (productDetails) { + return { + wishListDetails: product, + productInfo: { + productId: productDetails.id, + name: productDetails.name, + image: productDetails.images, + newPrice: productDetails.newPrice, + vendorId: productDetails.vendor, + }, + }; + } + }) + ); - } catch (error) { - return res.status(500).json({ error: 'Internal server error' }); - } -} \ No newline at end of file + return res.status(200).json({ message: 'Products retrieved', productsForBuyer: buyerWishProducts }); + } catch (error) { + return res.status(500).json({ error: 'Internal server error' }); + } +}; diff --git a/src/services/wishListServices/removeProducts.ts b/src/services/wishListServices/removeProducts.ts index cb99c0f..b42052f 100644 --- a/src/services/wishListServices/removeProducts.ts +++ b/src/services/wishListServices/removeProducts.ts @@ -2,22 +2,20 @@ import { Request, Response } from 'express'; import { getRepository } from 'typeorm'; import { wishList } from '../../entities/wishList'; -export const removeProductService = async (req:Request,res:Response)=>{ - try { +export const removeProductService = async (req: Request, res: Response) => { + try { + const id = parseInt(req.params.id); + const wishListRepository = getRepository(wishList); - const id = parseInt(req.params.id); - const wishListRepository = getRepository(wishList); + const product = await wishListRepository.findOne({ where: { id } }); - const product = await wishListRepository.findOne({where: { id }}); - - if(!product){ - return res.status(404).json({message: "Product not found in wish list"}); - } - - await wishListRepository.remove(product); - return res.status(200).json({ message: "Product removed from wish list" }); - - } catch (error) { - return res.status(500).json({ error: 'Internal server error' }); + if (!product) { + return res.status(404).json({ message: 'Product not found in wish list' }); } -} \ No newline at end of file + + await wishListRepository.remove(product); + return res.status(200).json({ message: 'Product removed from wish list' }); + } catch (error) { + return res.status(500).json({ error: 'Internal server error' }); + } +}; diff --git a/src/startups/getSwaggerServer.ts b/src/startups/getSwaggerServer.ts index efe12fa..6a7416d 100644 --- a/src/startups/getSwaggerServer.ts +++ b/src/startups/getSwaggerServer.ts @@ -7,7 +7,7 @@ function getSwaggerServer (): string { return process.env.SWAGGER_SERVER; } - return `http://localhost:${process.env.PORT}/api/v1`; + return `http://localhost:${process.env.PORT}`; } export { getSwaggerServer }; diff --git a/src/train.ts b/src/train.ts new file mode 100644 index 0000000..732324e --- /dev/null +++ b/src/train.ts @@ -0,0 +1,43 @@ + +import fs from 'fs' + +import { NlpManager } from 'node-nlp'; + +export const manager = new NlpManager({ languages: ["en"] }); + +async function trainManager() { + const intentFiles = fs.readdirSync('./intents'); + + for (const file of intentFiles) { + try { + const filePath = `./intents/${file}`; + const data = await fs.promises.readFile(filePath, 'utf8'); + const jsonData = JSON.parse(data); + + const intent = file.replace('.json', ''); + + for (const utterances of jsonData.utterances) { + manager.addDocument('en', utterances, intent); + } + + for (const responses of jsonData.responses) { + manager.addAnswer('en', intent, responses); + } + } catch (error) { + console.error(`Error processing intent file ${file}:`, error); + } + } + + await manager.train(); +} + +trainManager() + .then(async () => { + manager.save(); + }) + .catch((error) => console.error('Error training NLP manager:', error)); + + module.exports = { + manager, + trainManager + }; diff --git a/src/utils/auth.ts b/src/utils/auth.ts index 91874e3..623883f 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -1,72 +1,66 @@ /* eslint-disable camelcase */ import passport from 'passport'; -import { Strategy } from "passport-google-oauth20"; +import { Strategy } from 'passport-google-oauth20'; import { User } from '../entities/User'; import { getRepository } from 'typeorm'; import bcrypt from 'bcrypt'; -import "../utils/auth"; +import '../utils/auth'; passport.use( - new Strategy( - { - clientID: process.env.GOOGLE_CLIENT_ID as string, - clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, - callbackURL: 'http://localhost:6890/user/auth/google/callback/', - scope: ['email', 'profile'], - }, - async (accessToken: any, refreshToken: any, profile: any, cb: any) => { - const userRepository = getRepository(User); - const { family_name, - name, - picture, - email, - email_verified + new Strategy( + { + clientID: process.env.GOOGLE_CLIENT_ID as string, + clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, + callbackURL: 'http://localhost:6890/user/auth/google/callback/', + scope: ['email', 'profile'], + }, + async (accessToken: any, refreshToken: any, profile: any, cb: any) => { + const userRepository = getRepository(User); + const { family_name, name, picture, email, email_verified } = profile._json; + const { familyName, givenName } = profile.name; - } = profile._json; - const { familyName, givenName } = profile.name; + if (email || givenName || family_name || picture) { + try { + // Check for existing user + const existingUser = await userRepository.findOneBy({ email }); - if (email || givenName || family_name || picture) { - try { - // Check for existing user - const existingUser = await userRepository.findOneBy({ email }); + if (existingUser) { + return await cb(null, existingUser); + } + const saltRounds = 10; + const hashedPassword = await bcrypt.hash('password', saltRounds); + const newUser = new User(); + newUser.firstName = givenName; + newUser.lastName = family_name ?? familyName ?? 'undefined'; + newUser.email = email; + newUser.userType = 'Buyer'; + newUser.photoUrl = picture; + newUser.gender = 'Not specified'; + newUser.phoneNumber = 'Not specified'; + newUser.password = hashedPassword; + newUser.verified = email_verified; - if (existingUser) { - return await cb(null, existingUser); - } - const saltRounds = 10; - const hashedPassword = await bcrypt.hash("password", saltRounds); - const newUser = new User(); - newUser.firstName = givenName; - newUser.lastName = family_name ?? familyName ?? "undefined"; - newUser.email = email; - newUser.userType = 'Buyer'; - newUser.photoUrl = picture; - newUser.gender = "Not specified"; - newUser.phoneNumber = "Not specified"; - newUser.password = hashedPassword; - newUser.verified = email_verified; - - await userRepository.save(newUser); - return await cb(null, newUser); - } catch (error) { - console.error(error); - return await cb(error, null); - } - } - return await cb(null, profile, { message: 'Missing required profile information' }); + await userRepository.save(newUser); + return await cb(null, newUser); + } catch (error) { + console.error(error); + return await cb(error, null); } - ) + } + return await cb(null, profile, { message: 'Missing required profile information' }); + } + ) ); passport.serializeUser((user: any, cb) => { - cb(null, user.id); + cb(null, user.id); }); passport.deserializeUser(async (id: any, cb) => { - const userRepository = getRepository(User); - try { - const user = await userRepository.findOneBy({id}); - cb(null, user); - } catch (error) { - cb(error); - } + const userRepository = getRepository(User); + try { + const user = await userRepository.findOneBy({ id }); + cb(null, user); + } catch (error) { + cb(error); + } }); diff --git a/src/utils/getNotifications.ts b/src/utils/getNotifications.ts new file mode 100644 index 0000000..5cc6309 --- /dev/null +++ b/src/utils/getNotifications.ts @@ -0,0 +1,27 @@ +import { getRepository } from "typeorm"; +import { Notification } from "../entities/Notification"; + +export const getNotifications = async (userId: string) => { + try { + const notificationRepository = getRepository(Notification); + + const notifications = await notificationRepository.findOne({ + where: { + user: { + id: userId + } + }, + relations: { + allNotifications: true + } + }); + + if (!notifications) { + return {}; + } + return notifications; + } catch (error) { + console.error((error as Error).message); + return {}; + } +}; \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts index 27e92ae..fdc7feb 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -5,16 +5,16 @@ * @param currency - The currency code (e.g., 'USD', 'EUR'). Defaults to 'USD'. * @returns The formatted currency string. */ -export function formatMoney(amount: number , currency: string = 'RWF'): string { - return amount.toLocaleString('en-US', { style: 'currency', currency }); - } - /** - * Format a date string into a more readable format. - * @param dateString - The date string to format. - * @returns The formatted date string. - */ - export function formatDate(dateString: Date): string { - const options: Intl.DateTimeFormatOptions = { year: 'numeric', month: 'long', day: 'numeric' }; - const date = new Date(dateString); - return date.toLocaleDateString('en-US', options); - } \ No newline at end of file +export function formatMoney (amount: number, currency: string = 'RWF'): string { + return amount.toLocaleString('en-US', { style: 'currency', currency }); +} +/** + * Format a date string into a more readable format. + * @param dateString - The date string to format. + * @returns The formatted date string. + */ +export function formatDate (dateString: Date): string { + const options: Intl.DateTimeFormatOptions = { year: 'numeric', month: 'long', day: 'numeric' }; + const date = new Date(dateString); + return date.toLocaleDateString('en-US', options); +} diff --git a/src/utils/sendNotification.ts b/src/utils/sendNotification.ts new file mode 100644 index 0000000..de46d32 --- /dev/null +++ b/src/utils/sendNotification.ts @@ -0,0 +1,58 @@ +import { Notification } from "../entities/Notification"; +import { NotificationItem } from "../entities/NotificationItem"; +import { getRepository } from 'typeorm'; +import { User } from "../entities/User"; +import { getIO } from "./socket"; +import { getNotifications } from "./getNotifications"; + +interface noticationInfo{ + content: string; + type: 'product'|'cart'|'order'|'user'|'wish list'|'coupon'; + user: User; + link?: string; +} + +export const sendNotification = async (data: noticationInfo) =>{ + try { + const notificationRepo = getRepository(Notification) + const notificationItemRepo = getRepository(NotificationItem); + + let notification = await notificationRepo + .findOne({ + where: { + user: {id: data.user.id}}, + relations: ['allNotifications', 'user'] + }); + + if(!notification){ + notification = new Notification(); + notification.user = data.user; + await notificationRepo.save(notification); + } + + const notificationItem = new NotificationItem(); + notificationItem.notification = notification; + notificationItem.content = data.content; + notificationItem.type = data.type; + if(data.link){ + notificationItem.link = data.link + } + await notificationItemRepo.save(notificationItem); + + //Update numbers + notification = await notificationRepo + .findOne({where: {id: notification.id, user: {id: data.user.id}}, relations: ['allNotifications', 'user'] }); + + if(notification){ + notification.updateUnread(); + await notificationRepo.save(notification); + } + + getIO().emit('notification', { + action: `${data.user.email} notification`, + notifications: await getNotifications(data.user.id) + }); + } catch (error) { + console.log(error); + } +}; \ No newline at end of file diff --git a/src/utils/sendOrderMail.ts b/src/utils/sendOrderMail.ts index a58fe09..72ee5b0 100644 --- a/src/utils/sendOrderMail.ts +++ b/src/utils/sendOrderMail.ts @@ -29,10 +29,7 @@ const sendMail = async (message: Message) => { }, }); - const { subject, fullName, email, products, totalAmount, - quantity, - orderDate, - address } = message; + const { subject, fullName, email, products, totalAmount, quantity, orderDate, address } = message; const mailOptions = { to: email, @@ -180,14 +177,18 @@ const sendMail = async (message: Message) => { Quantity Total - ${products.map((product: Product) => ` + ${products + .map( + (product: Product) => ` ${product.name} ${formatMoney(product.newPrice)} ${product.quantity} ${product.quantity * product.newPrice} - `).join('')} + ` + ) + .join('')} Total ${totalAmount} @@ -211,4 +212,4 @@ const sendMail = async (message: Message) => { } }; -export default sendMail; \ No newline at end of file +export default sendMail; diff --git a/src/utils/sendOrderMailUpdated.ts b/src/utils/sendOrderMailUpdated.ts index ed2cf83..adddc9a 100644 --- a/src/utils/sendOrderMailUpdated.ts +++ b/src/utils/sendOrderMailUpdated.ts @@ -29,10 +29,7 @@ const sendMail = async (message: Message) => { }, }); - const { subject, fullName, email, products, totalAmount, - quantity, - orderDate, - address } = message; + const { subject, fullName, email, products, totalAmount, quantity, orderDate, address } = message; const mailOptions = { to: email, @@ -180,14 +177,18 @@ const sendMail = async (message: Message) => { Quantity Total - ${products.map((product: Product) => ` + ${products + .map( + (product: Product) => ` ${product.name} ${formatMoney(product.newPrice)} ${product.quantity} ${product.quantity * product.newPrice} - `).join('')} + ` + ) + .join('')} Total ${totalAmount} @@ -211,4 +212,4 @@ const sendMail = async (message: Message) => { } }; -export default sendMail; \ No newline at end of file +export default sendMail; diff --git a/src/utils/socket.ts b/src/utils/socket.ts new file mode 100644 index 0000000..e32ae19 --- /dev/null +++ b/src/utils/socket.ts @@ -0,0 +1,21 @@ +import { Server as HTTPServer } from 'http'; +import { Server as SocketIOServer } from 'socket.io'; + +let io: SocketIOServer | undefined; + +export const init = (httpServer: HTTPServer): SocketIOServer => { + io = new SocketIOServer(httpServer, { + cors: { + origin: '*', + methods: ['GET', 'POST', 'DELETE', 'PUT'], + }, + }); + return io; +}; + +export const getIO = (): SocketIOServer => { + if (!io) { + throw new Error('Socket.io not initialized!'); + } + return io; +}; diff --git a/tsconfig.json b/tsconfig.json index 4665a6d..7e48aed 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,108 +1,108 @@ { - "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - /* Language and Environment */ - "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - "experimentalDecorators": true /* Enable experimental support for legacy experimental decorators. */, - "emitDecoratorMetadata": true /* Emit design-type metadata for decorated declarations in source files. */, - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - /* Modules */ - "module": "commonjs" /* Specify what module code is generated. */, - "rootDir": "./src" /* Specify the root folder within your source files. */, - // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - "types": [ - "node", - "jest", - "express", - "joi" - ] /* Specify type package names to be included without being referenced in a source file. */, - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ - // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ - // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ - // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - /* JavaScript Support */ - "allowJs": true /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */, - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./dist" /* Specify an output folder for all emitted files. */, - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, - /* Type Checking */ - "strict": true /* Enable all strict type-checking options. */, - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - }, - "include": ["src/**/*.ts"], - "exclude": ["node_modules"] - } \ No newline at end of file + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + /* Language and Environment */ + "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + "experimentalDecorators": true /* Enable experimental support for legacy experimental decorators. */, + "emitDecoratorMetadata": true /* Emit design-type metadata for decorated declarations in source files. */, + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + /* Modules */ + "module": "commonjs" /* Specify what module code is generated. */, + "rootDir": "./src" /* Specify the root folder within your source files. */, + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + "typeRoots": ["node_modules/@types", "./src"], /* Specify multiple folders that act like './node_modules/@types'. */ + "types": [ + "node", + "jest", + "express", + "node-nlp" + ] /* Specify type package names to be included without being referenced in a source file. */, + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + /* JavaScript Support */ + "allowJs": true /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */, + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./dist" /* Specify an output folder for all emitted files. */, + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + /* Type Checking */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules"] +}