Compare commits

...

2 Commits

Author SHA1 Message Date
Сюткина Дарья Александровна (4047910) fcd103b8c8 Merge branch 'master' of https://customer-vertexa-gitea.devbay.tech/Bbuddy/bbuddy-ui 2024-01-17 18:42:05 +04:00
Сюткина Дарья Александровна (4047910) 4ecd48146e feat: add auth modal 2024-01-17 18:41:46 +04:00
64 changed files with 1898 additions and 615 deletions

1
.gitignore vendored
View File

@ -15,6 +15,7 @@
# production # production
/dist /dist
/build
# ide # ide
/.idea /.idea

View File

@ -30,6 +30,37 @@
}, },
"Messages": { "Messages": {
"title": "Messages" "title": "Messages"
},
"Settings": {
"title": "Profile Settings",
"photo-desc": "Add a real photo, as a person's face is always more credible.",
"name": "Name",
"surname": "Surname",
"birthday": "Date of Birth",
"email": "E-mail",
"change-password": "Change Password",
"save": "Save",
"old-password": "Old Password",
"new-password": "New Password",
"confirm-password": "Confirm Password"
},
"LegalInformation": {
"title": "Legal Information"
},
"Support": {
"title": "Help & Support"
},
"Notifications": {
"title": "Notifications",
"read": "Read",
"delete": "Delete"
},
"Sessions": {
"upcoming-sessions": "Upcoming Sessions",
"sessions-requested": "Sessions Requested",
"recent-sessions": "Recent Sessions",
"topic": "Topic",
"day-start": "Day start"
} }
}, },
"Footer": { "Footer": {

View File

@ -31,6 +31,37 @@
}, },
"Messages": { "Messages": {
"title": "Messages" "title": "Messages"
},
"Settings": {
"title": "Profile Settings",
"photo-desc": "Add a real photo, as a person's face is always more credible.",
"name": "Name",
"surname": "Surname",
"birthday": "Date of Birth",
"email": "E-mail",
"change-password": "Change Password",
"save": "Save",
"old-password": "Old Password",
"new-password": "New Password",
"confirm-password": "Confirm Password"
},
"LegalInformation": {
"title": "Legal Information"
},
"Support": {
"title": "Help & Support"
},
"Notifications": {
"title": "Notifications",
"read": "Read",
"delete": "Delete"
},
"Sessions": {
"upcoming-sessions": "Upcoming Sessions",
"sessions-requested": "Sessions Requested",
"recent-sessions": "Recent Sessions",
"topic": "Topic",
"day-start": "Day start"
} }
}, },
"Footer": { "Footer": {

View File

@ -30,6 +30,37 @@
}, },
"Messages": { "Messages": {
"title": "Messages" "title": "Messages"
},
"Settings": {
"title": "Profile Settings",
"photo-desc": "Add a real photo, as a person's face is always more credible.",
"name": "Name",
"surname": "Surname",
"birthday": "Date of Birth",
"email": "E-mail",
"change-password": "Change Password",
"save": "Save",
"old-password": "Old Password",
"new-password": "New Password",
"confirm-password": "Confirm Password"
},
"LegalInformation": {
"title": "Legal Information"
},
"Support": {
"title": "Help & Support"
},
"Notifications": {
"title": "Notifications",
"read": "Read",
"delete": "Delete"
},
"Sessions": {
"upcoming-sessions": "Upcoming Sessions",
"sessions-requested": "Sessions Requested",
"recent-sessions": "Recent Sessions",
"topic": "Topic",
"day-start": "Day start"
} }
}, },
"Footer": { "Footer": {

View File

@ -30,6 +30,37 @@
}, },
"Messages": { "Messages": {
"title": "Messages" "title": "Messages"
},
"Settings": {
"title": "Profile Settings",
"photo-desc": "Add a real photo, as a person's face is always more credible.",
"name": "Name",
"surname": "Surname",
"birthday": "Date of Birth",
"email": "E-mail",
"change-password": "Change Password",
"save": "Save",
"old-password": "Old Password",
"new-password": "New Password",
"confirm-password": "Confirm Password"
},
"LegalInformation": {
"title": "Legal Information"
},
"Support": {
"title": "Help & Support"
},
"Notifications": {
"title": "Notifications",
"read": "Read",
"delete": "Delete"
},
"Sessions": {
"upcoming-sessions": "Upcoming Sessions",
"sessions-requested": "Sessions Requested",
"recent-sessions": "Recent Sessions",
"topic": "Topic",
"day-start": "Day start"
} }
}, },
"Footer": { "Footer": {

View File

@ -30,6 +30,37 @@
}, },
"Messages": { "Messages": {
"title": "Messages" "title": "Messages"
},
"Settings": {
"title": "Profile Settings",
"photo-desc": "Add a real photo, as a person's face is always more credible.",
"name": "Name",
"surname": "Surname",
"birthday": "Date of Birth",
"email": "E-mail",
"change-password": "Change Password",
"save": "Save",
"old-password": "Old Password",
"new-password": "New Password",
"confirm-password": "Confirm Password"
},
"LegalInformation": {
"title": "Legal Information"
},
"Support": {
"title": "Help & Support"
},
"Notifications": {
"title": "Notifications",
"read": "Read",
"delete": "Delete"
},
"Sessions": {
"upcoming-sessions": "Upcoming Sessions",
"sessions-requested": "Sessions Requested",
"recent-sessions": "Recent Sessions",
"topic": "Topic",
"day-start": "Day start"
} }
}, },
"Footer": { "Footer": {

View File

@ -23,13 +23,44 @@
"work-with-us": "Работать с нами" "work-with-us": "Работать с нами"
}, },
"WorkWithUs": { "WorkWithUs": {
"title": "Become a BBuddy Expert", "title": "Стань BBuddy экспертом",
"insert-info": "Insert your personal information to start your journey as a BBuddy Expert", "insert-info": "Insert your personal information to start your journey as a BBuddy Expert",
"start": "Get Started", "start": "Начать",
"base-text": "Your info can either be added or amended at anytime" "base-text": "Your info can either be added or amended at anytime"
}, },
"Messages": { "Messages": {
"title": "Сообщения" "title": "Сообщения"
},
"Settings": {
"title": "Настройки пользователя",
"photo-desc": "Добавьте реальное фото, это вызывает больше доверия",
"name": "Имя",
"surname": "Фамилия",
"birthday": "Дата рождения",
"email": "E-mail",
"change-password": "Изменить пароль",
"save": "Сохранить изменения",
"old-password": "Старый пароль",
"new-password": "Новый пароль",
"confirm-password": "Подтверждение пароля"
},
"LegalInformation": {
"title": "Legal Information"
},
"Support": {
"title": "Помощь и поддержка"
},
"Notifications": {
"title": "Оповещения",
"read": "Смотреть",
"delete": "Удалить"
},
"Sessions": {
"upcoming-sessions": "Upcoming Sessions",
"sessions-requested": "Sessions Requested",
"recent-sessions": "Recent Sessions",
"topic": "Тема",
"day-start": "Дата начала"
} }
}, },
"Footer": { "Footer": {

View File

@ -14,9 +14,27 @@ const nextConfig = {
sassOptions: { sassOptions: {
includePaths: [path.join(__dirname, 'styles')], includePaths: [path.join(__dirname, 'styles')],
}, },
images: {
unoptimized: true
},
experimental: {
taint: true,
typedRoutes: true
},
output: 'export', output: 'export',
cleanDistDir: true, distDir: 'dist',
distDir: 'dist' poweredByHeader: false,
productionBrowserSourceMaps: true,
trailingSlash: true
// redirects: async () => {
// return [
// {
// source: '/en/',
// destination: '/en.html',
// permanent: true
// }
// ]
// }
}; };
module.exports = withNextIntl(nextConfig); module.exports = withNextIntl(nextConfig);

152
package-lock.json generated
View File

@ -11,6 +11,7 @@
"@ant-design/cssinjs": "^1.18.1", "@ant-design/cssinjs": "^1.18.1",
"@ant-design/icons": "^5.2.6", "@ant-design/icons": "^5.2.6",
"antd": "^5.12.1", "antd": "^5.12.1",
"axios": "^1.6.5",
"next": "14.0.3", "next": "14.0.3",
"next-intl": "^3.3.1", "next-intl": "^3.3.1",
"react": "^18", "react": "^18",
@ -1123,6 +1124,11 @@
"has-symbols": "^1.0.3" "has-symbols": "^1.0.3"
} }
}, },
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/autoprefixer": { "node_modules/autoprefixer": {
"version": "10.4.16", "version": "10.4.16",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz",
@ -1181,6 +1187,16 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/axios": {
"version": "1.6.5",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz",
"integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==",
"dependencies": {
"follow-redirects": "^1.15.4",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/axobject-query": { "node_modules/axobject-query": {
"version": "3.2.1", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
@ -1403,6 +1419,17 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true "dev": true
}, },
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/compute-scroll-into-view": { "node_modules/compute-scroll-into-view": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz", "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz",
@ -1524,6 +1551,14 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/dequal": { "node_modules/dequal": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@ -2243,6 +2278,25 @@
"integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
"dev": true "dev": true
}, },
"node_modules/follow-redirects": {
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/for-each": { "node_modules/for-each": {
"version": "0.3.3", "version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
@ -2252,6 +2306,19 @@
"is-callable": "^1.1.3" "is-callable": "^1.1.3"
} }
}, },
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/fraction.js": { "node_modules/fraction.js": {
"version": "4.3.7", "version": "4.3.7",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
@ -3180,6 +3247,25 @@
"node": ">=8.6" "node": ">=8.6"
} }
}, },
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -3624,6 +3710,11 @@
"react-is": "^16.13.1" "react-is": "^16.13.1"
} }
}, },
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/punycode": { "node_modules/punycode": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@ -5938,6 +6029,11 @@
"has-symbols": "^1.0.3" "has-symbols": "^1.0.3"
} }
}, },
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"autoprefixer": { "autoprefixer": {
"version": "10.4.16", "version": "10.4.16",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz",
@ -5964,6 +6060,16 @@
"integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==",
"dev": true "dev": true
}, },
"axios": {
"version": "1.6.5",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz",
"integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==",
"requires": {
"follow-redirects": "^1.15.4",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"axobject-query": { "axobject-query": {
"version": "3.2.1", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
@ -6113,6 +6219,14 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true "dev": true
}, },
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"compute-scroll-into-view": { "compute-scroll-into-view": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz", "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz",
@ -6211,6 +6325,11 @@
"object-keys": "^1.1.1" "object-keys": "^1.1.1"
} }
}, },
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
},
"dequal": { "dequal": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@ -6776,6 +6895,11 @@
"integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
"dev": true "dev": true
}, },
"follow-redirects": {
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw=="
},
"for-each": { "for-each": {
"version": "0.3.3", "version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
@ -6785,6 +6909,16 @@
"is-callable": "^1.1.3" "is-callable": "^1.1.3"
} }
}, },
"form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
},
"fraction.js": { "fraction.js": {
"version": "4.3.7", "version": "4.3.7",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
@ -7458,6 +7592,19 @@
"picomatch": "^2.3.1" "picomatch": "^2.3.1"
} }
}, },
"mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
},
"mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"requires": {
"mime-db": "1.52.0"
}
},
"minimatch": { "minimatch": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -7754,6 +7901,11 @@
"react-is": "^16.13.1" "react-is": "^16.13.1"
} }
}, },
"proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"punycode": { "punycode": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",

View File

@ -3,7 +3,7 @@
"version": "0.0.1", "version": "0.0.1",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev -p 4200",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint" "lint": "next lint"
@ -12,6 +12,7 @@
"@ant-design/cssinjs": "^1.18.1", "@ant-design/cssinjs": "^1.18.1",
"@ant-design/icons": "^5.2.6", "@ant-design/icons": "^5.2.6",
"antd": "^5.12.1", "antd": "^5.12.1",
"axios": "^1.6.5",
"next": "14.0.3", "next": "14.0.3",
"next-intl": "^3.3.1", "next-intl": "^3.3.1",
"react": "^18", "react": "^18",

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 855 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,11 @@
<svg width="192" height="60" viewBox="0 0 192 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M37.0913 31.7816V13.8849C35.9805 13.613 34.829 13.477 33.6233 13.477C32.7022 13.477 31.9029 13.5314 31.1849 13.6402L31.2391 26.3147C34.1788 26.4371 36.1431 28.9121 36.1431 31.7816C36.1431 34.719 34.0839 37.2621 31.0223 37.2621C27.893 37.2621 25.8339 34.719 25.8339 31.7816C25.8339 31.4688 25.861 31.1696 25.9016 30.884L25.7662 0.380859L12.9237 1.4416V31.7816C12.9237 36.4597 14.2648 40.3627 16.5814 43.3546L39.2047 40.9611C37.85 38.3772 37.0913 35.2902 37.0913 31.7816Z" fill="#35E2CF"/>
<path d="M22.542 48.1416C25.2243 49.4199 28.3672 50.0863 31.8216 50.0863C36.1295 50.0863 40.2478 48.7263 43.4855 46.1289C43.4042 46.0745 43.3365 46.0065 43.2687 45.9385L22.542 48.1416Z" fill="#35E2CF"/>
<path d="M43.4855 46.1291C46.7367 48.6858 51.0446 50.0865 55.9893 50.0865C61.7602 50.0865 67.2061 47.6522 70.6063 43.0557L43.77 45.8979C43.6887 45.9795 43.5803 46.0475 43.4855 46.1291Z" fill="#5AADCC"/>
<path d="M43.2687 45.9519C43.3365 46.0063 43.4178 46.0743 43.4855 46.1423C43.5803 46.0607 43.6887 45.9791 43.7835 45.9111L43.2687 45.9519Z" fill="#5AADCC"/>
<path d="M57.791 13.4633C56.8698 13.4633 56.0705 13.5177 55.3525 13.6265L55.4067 26.301C58.3464 26.4234 60.3107 28.8985 60.3107 31.7679C60.3107 34.7054 58.2516 37.2484 55.19 37.2484C52.0607 37.2484 50.0015 34.7054 50.0015 31.7679C50.0015 31.4551 50.0286 31.1559 50.0693 30.8568L50.0015 15.6664V0.367188H49.9338L37.0913 1.42793V13.8713V31.7815C37.0913 35.3037 37.8364 38.3772 39.1911 40.9746L48.2269 40.0227L73.3699 37.3708C73.8711 35.5757 74.1556 33.6038 74.1556 31.4551C74.1692 22.4932 67.3144 13.4633 57.791 13.4633Z" fill="#5AADCC"/>
<path d="M81.2813 26.0283V42.2115C81.2813 46.5904 83.0288 49.0655 87.1877 49.0655C91.7395 49.0655 94.6385 47.42 94.6385 37.6013V26.6131L97.4427 26.0283V50.9966L94.6385 51.391V47.352H94.5437C93.5818 49.5415 90.6692 51.5814 86.9439 51.5814C80.7936 51.5814 78.4771 48.0863 78.4771 42.5514V26.6131L81.2813 26.0283Z" fill="#5AADCC"/>
<path d="M123.06 16.4139L125.864 15.8291V51.0104L123.06 51.5952V46.9306H122.965C121.123 49.3649 117.397 51.5952 113.428 51.5952C106.357 51.5952 100.748 46.4003 100.748 38.7167C100.748 31.5771 106.316 26.0422 113.333 26.0422C117.302 26.0422 120.594 28.1364 122.965 30.8563H123.06V16.4139ZM113.184 28.8572C107.427 28.8572 103.552 33.8617 103.552 38.7167C103.552 44.4012 107.427 48.7801 113.279 48.7801C118.698 48.7801 123.06 44.17 123.06 38.8663C123.06 32.5426 118.508 28.8572 113.184 28.8572Z" fill="#5AADCC"/>
<path d="M151.712 16.4139L154.516 15.8291V51.0104L151.712 51.5952V46.9306H151.617C149.774 49.3649 146.049 51.5952 142.08 51.5952C135.008 51.5952 129.4 46.4003 129.4 38.7167C129.4 31.5771 134.968 26.0422 141.985 26.0422C145.954 26.0422 149.246 28.1364 151.617 30.8563H151.712V16.4139ZM141.836 28.8572C136.078 28.8572 132.204 33.8617 132.204 38.7167C132.204 44.4012 136.078 48.7801 141.931 48.7801C147.349 48.7801 151.712 44.17 151.712 38.8663C151.712 32.5426 147.16 28.8572 141.836 28.8572Z" fill="#5AADCC"/>
<path d="M155.288 26.6131L158.051 26.0283L167.778 43.8162L176.299 26.0283L179.103 26.6131L162.888 59.9449L160.178 59.3601L166.18 46.7808L155.288 26.6131Z" fill="#5AADCC"/>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

BIN
public/images/popular2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
public/images/popular3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
public/images/popular4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
public/images/popular5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
public/images/popular6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

14
src/actions/auth.ts Normal file
View File

@ -0,0 +1,14 @@
import { AxiosResponse } from 'axios';
import { apiClient } from '../lib/apiClient';
export const getAuth = (login: string, password: string, locale: string ): Promise<AxiosResponse<{ jwtToken: string }>> => (
apiClient.post(
'/auth/login',
{ login, password },
{
headers: {
'X-User-Language': locale
}
}
)
);

32
src/actions/experts.ts Normal file
View File

@ -0,0 +1,32 @@
import { apiClient } from '../lib/apiClient';
import { Filter, ExpertsData, ExpertDetails } from '../types/experts';
export const getExpertsList = async (filter: Filter, locale: string) => {
const response = await apiClient.post(
'/home/coachsearch',
{ ...filter },
{
headers: {
'X-User-Language': locale,
Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IkpXVCJ9.eyJuYW1laWQiOiIxNzIiLCJuYmYiOjE3MDM2ODMyMDgsImV4cCI6MTczNTIxOTIwOCwiaWF0IjoxNzAzNjgzMjA4fQ.KgnYfKO7oVFLlDuKhfyNN6RAaXKdeSzJd7F4r6_15AA'
}
}
);
return response.data?.coaches as ExpertsData || null;
};
export const getExpertById = async (id: string, locale: string) => {
const response = await apiClient.post(
'/home/coachdetails',
{ id },
{
headers: {
'X-User-Language': locale,
Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IkpXVCJ9.eyJuYW1laWQiOiIxNzIiLCJuYmYiOjE3MDM2ODMyMDgsImV4cCI6MTczNTIxOTIwOCwiaWF0IjoxNzAzNjgzMjA4fQ.KgnYfKO7oVFLlDuKhfyNN6RAaXKdeSzJd7F4r6_15AA'
}
}
);
return response.data as ExpertDetails || null;
};

View File

@ -9,7 +9,7 @@ export default function Directions() {
<div className="main-popular__coll"> <div className="main-popular__coll">
<div className="b-popular"> <div className="b-popular">
<div className="b-popular__image"> <div className="b-popular__image">
<img className="" src="/images/popular.png" alt=""/> <img className="" src="/images/popular1.png" alt=""/>
</div> </div>
<div className="b-popular__inner"> <div className="b-popular__inner">
<div className="b-popular__title">Work Life Balance</div> <div className="b-popular__title">Work Life Balance</div>
@ -23,10 +23,10 @@ export default function Directions() {
<div className="main-popular__coll"> <div className="main-popular__coll">
<div className="b-popular"> <div className="b-popular">
<div className="b-popular__image"> <div className="b-popular__image">
<img className="" src="/images/popular.png" alt=""/> <img className="" src="/images/popular2.png" alt=""/>
</div> </div>
<div className="b-popular__inner"> <div className="b-popular__inner">
<div className="b-popular__title">Work Life Balance</div> <div className="b-popular__title">Strategic Session</div>
<div className="b-popular__wrap-link"> <div className="b-popular__wrap-link">
<a className="b-popular__link" href="">23 experts</a> | <a className="b-popular__link" href="">23 experts</a> |
<a className="b-popular__link" href="">245 offers</a> <a className="b-popular__link" href="">245 offers</a>
@ -37,10 +37,10 @@ export default function Directions() {
<div className="main-popular__coll d-none d-md-block"> <div className="main-popular__coll d-none d-md-block">
<div className="b-popular"> <div className="b-popular">
<div className="b-popular__image"> <div className="b-popular__image">
<img className="" src="/images/popular.png" alt=""/> <img className="" src="/images/popular3.png" alt=""/>
</div> </div>
<div className="b-popular__inner"> <div className="b-popular__inner">
<div className="b-popular__title">Work Life Balance</div> <div className="b-popular__title">Personal Growth</div>
<div className="b-popular__wrap-link"> <div className="b-popular__wrap-link">
<a className="b-popular__link" href="">23 experts</a> | <a className="b-popular__link" href="">23 experts</a> |
<a className="b-popular__link" href="">245 offers</a> <a className="b-popular__link" href="">245 offers</a>
@ -51,10 +51,10 @@ export default function Directions() {
<div className="main-popular__coll d-none d-lg-block"> <div className="main-popular__coll d-none d-lg-block">
<div className="b-popular"> <div className="b-popular">
<div className="b-popular__image"> <div className="b-popular__image">
<img className="" src="/images/popular.png" alt=""/> <img className="" src="/images/popular4.png" alt=""/>
</div> </div>
<div className="b-popular__inner"> <div className="b-popular__inner">
<div className="b-popular__title">Work Life Balance</div> <div className="b-popular__title">Career Planning</div>
<div className="b-popular__wrap-link"> <div className="b-popular__wrap-link">
<a className="b-popular__link" href="">23 experts</a> | <a className="b-popular__link" href="">23 experts</a> |
<a className="b-popular__link" href="">245 offers</a> <a className="b-popular__link" href="">245 offers</a>
@ -65,10 +65,10 @@ export default function Directions() {
<div className="main-popular__coll d-none d-lg-block"> <div className="main-popular__coll d-none d-lg-block">
<div className="b-popular"> <div className="b-popular">
<div className="b-popular__image"> <div className="b-popular__image">
<img className="" src="/images/popular.png" alt=""/> <img className="" src="/images/popular5.png" alt=""/>
</div> </div>
<div className="b-popular__inner"> <div className="b-popular__inner">
<div className="b-popular__title">Work Life Balance</div> <div className="b-popular__title">Executive Coaching</div>
<div className="b-popular__wrap-link"> <div className="b-popular__wrap-link">
<a className="b-popular__link" href="">23 experts</a> | <a className="b-popular__link" href="">23 experts</a> |
<a className="b-popular__link" href="">245 offers</a> <a className="b-popular__link" href="">245 offers</a>
@ -79,10 +79,10 @@ export default function Directions() {
<div className="main-popular__coll d-none d-xl-block"> <div className="main-popular__coll d-none d-xl-block">
<div className="b-popular"> <div className="b-popular">
<div className="b-popular__image"> <div className="b-popular__image">
<img className="" src="/images/popular.png" alt=""/> <img className="" src="/images/popular6.png" alt=""/>
</div> </div>
<div className="b-popular__inner"> <div className="b-popular__inner">
<div className="b-popular__title">Work Life Balance</div> <div className="b-popular__title">Career Development</div>
<div className="b-popular__wrap-link"> <div className="b-popular__wrap-link">
<a className="b-popular__link" href="">23 experts</a> | <a className="b-popular__link" href="">23 experts</a> |
<a className="b-popular__link" href="">245 offers</a> <a className="b-popular__link" href="">245 offers</a>

View File

@ -1,76 +1,20 @@
'use client';
import React from 'react'; import React from 'react';
import { Avatar, List, Typography, Tag } from 'antd'; import { CustomPagination } from '../../../../components/view';
import { RightOutlined } from '@ant-design/icons'; import { ExpertsFilter } from '../../../../components/Experts/Filter';
import { Link } from '../../../../navigation'; import { ExpertsAdditionalFilter } from '../../../../components/Experts/AdditionalFilter';
import { ExpertsFilter, ExpertsAdditionalFilter } from '../../../../components/Experts'; import { ExpertsList } from '../../../../components/Experts/ExpertsList';
import { getExpertsList } from '../../../../actions/experts';
const { Text } = Typography; export default async function Experts({ params }: { params: { locale: string } }) {
const data = await getExpertsList({
const data = Array.from({ length: 10 }).map((_, i) => ({ "themesTagIds": [
href: '/', 1,2,3,4,5,6,7,8
name: 'Matthew Weeks', ],
avatar: '/images/person.png', "priceFrom": null,
duration: '45min', "priceTo": null,
price: '45$', "durationFrom": null,
skills: ['Engineering & Data', 'Soft skills', 'Interview'], "durationTo": null
speciality: 'Senior Software Engineer', }, params.locale);
specialityDesc: 'Auth0',
description: 'I have worked across a variety of organizations, lead teams, and delivered quality software for 8 years. In that time I\'ve worked as an independent consultant, at agencies as a team lead, and as a senior engineer at Auth0. I also host a podcast https://anchor.fm/work-in-programming where I break down how …'
}));
export default function Experts() {
const expertsList = (
<List
itemLayout="vertical"
size="large"
pagination={{
className: 'pagination',
onChange: (page) => {
console.log(page);
},
pageSize: 5
}}
dataSource={data}
renderItem={(item) => (
<List.Item key={item.name}>
<List.Item.Meta
avatar={<Avatar src={item.avatar} />}
title={item.name}
description={(
<div className="card-profile__header__price">
{item.price} <span>/ {item.duration}</span>
</div>
)}
/>
<div className="card-profile__skills">
<div className="skills__list">
{item.skills.map((skill) => <Tag color="#f50">{skill}</Tag>)}
<div className="skills__list__item">Engineering & Data</div>
<div className="skills__list__more">+6</div>
</div>
</div>
<div className="card-profile__title">{item.speciality}</div>
<div className="card-profile__subtitle">{item.specialityDesc}</div>
<div className="card-profile__desc">{item.description}</div>
<div className="card-profile__footer">
<Link href={item.href as any}>
<Text
copyable={{
icon: <RightOutlined key="copy-icon" style={{ fontSize: '15px' }}/>,
onCopy: undefined,
tooltips: false
}}
>
Details
</Text>
</Link>
</div>
</List.Item>
)}
/>
);
return ( return (
<div className="main-find"> <div className="main-find">
@ -83,54 +27,12 @@ export default function Experts() {
</div> </div>
<div className="row"> <div className="row">
<div className="col-xl-3 col-lg-4 d-none d-lg-block"> <div className="col-xl-3 col-lg-4 d-none d-lg-block">
<ExpertsFilter /> <ExpertsFilter locale={params.locale} />
</div> </div>
<div className="col-xl-9 col-lg-8 "> <div className="col-xl-9 col-lg-8 ">
<ExpertsAdditionalFilter /> <ExpertsAdditionalFilter />
<div className="search-result"> <ExpertsList data={data} locale={params.locale} />
<div className="card-profile"> <CustomPagination total={20} />
<div className="card-profile__header">
<div className="card-profile__header__portrait">
<img src="/images/person.png" className="" alt="" />
</div>
<div className="card-profile__header__inner">
<div className="card-profile__header__name">Matthew Weeks</div>
<div className="card-profile__header__price">
45$ <span>/ 45min</span>
</div>
</div>
</div>
<div className="card-profile__skills">
<div className="skills__list">
<div className="skills__list__item">Engineering & Data</div>
<div className="skills__list__item">Engineering & Data</div>
<div className="skills__list__more">+6</div>
</div>
</div>
<div className="card-profile__title">Senior Software Engineer</div>
<div className="card-profile__subtitle">Auth0</div>
<div className="card-profile__desc">
I have worked across a variety of organizations, lead teams, and delivered quality
software for 8 years. In that time I've worked as an independent consultant, at
agencies as a team lead, and as a senior engineer at Auth0. I also host a podcast
https://anchor.fm/work-in-programming where I break down how …
</div>
<div className="card-profile__footer">
<a href="#">Details
<img className="" src="/images/chevron-forward.svg" alt="" />
</a>
</div>
</div>
{expertsList}
</div>
<ul className="pagination">
<li className="page-item"><a className="page-link" href="#">1</a></li>
<li className="page-item active" aria-current="page">
<a className="page-link" href="#">2</a>
</li>
<li className="page-item"><a className="page-link" href="#">3</a></li>
<li className="page-item"><a className="page-link" href="#">4</a></li>
</ul>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,16 +1,16 @@
import React, { ReactNode } from 'react'; import React, { ReactNode } from 'react';
import { getTranslations } from 'next-intl/server'; // import { getTranslations } from 'next-intl/server';
//
export async function generateMetadata({ // export async function generateMetadata({
params: { locale } // params: { locale }
}: { params: { locale: string }}) { // }: { params: { locale: string }}) {
const t = await getTranslations({ locale, namespace: 'Main' }); // const t = await getTranslations({ locale, namespace: 'Main' });
//
return { // return {
title: t('title'), // title: t('title'),
description: t('description'), // description: t('description'),
}; // };
} // }
export default function MainLayout({ children, news, directions, experts }: { export default function MainLayout({ children, news, directions, experts }: {
children: ReactNode, children: ReactNode,

View File

@ -19,12 +19,20 @@ export default function Home() {
<p className="main-top__text">from a mentor, and getting your feet wet with coaching.</p> <p className="main-top__text">from a mentor, and getting your feet wet with coaching.</p>
</div> </div>
<div className="main-top__wrap-btn"> <div className="main-top__wrap-btn">
<a href="#" className="main-top__btn main-top__btn--apple"> <a
href="https://apps.apple.com/us/app/bbuddy/id6443601377?platform=iphone"
className="main-top__btn main-top__btn--apple"
target="_blank"
>
<img className="" src="/images/logo-apple.svg" alt=""/> <img className="" src="/images/logo-apple.svg" alt=""/>
<span className="line" /> <span className="line" />
AppStore AppStore
</a> </a>
<a href="#" className="main-top__btn main-top__btn--android"> <a
href="https://play.google.com/store/apps/details?id=com.bbuddy.whistle"
className="main-top__btn main-top__btn--android"
target="_blank"
>
<img className="" src="/images/logo-android.svg" alt=""/> <img className="" src="/images/logo-android.svg" alt=""/>
<span className="line" /> <span className="line" />
GooglePlay GooglePlay

View File

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import { useTranslations } from 'next-intl';
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'Bbuddy - Account - Information', title: 'Bbuddy - Account - Information',
@ -7,9 +8,68 @@ export const metadata: Metadata = {
}; };
export default function Information() { export default function Information() {
const t = useTranslations('Account.LegalInformation');
return ( return (
<div> <>
Страница Информации о пользователе <ol className="breadcrumb">
</div> <li className="breadcrumb-item active" aria-current="page">{t('title')}</li>
</ol>
<div className="base-text">
Welcome to the B BUDDY LTDs privacy policy. <br />
B BUDDY LTD respects your privacy and is committed to protecting your personal data. This privacy policy
will inform you as to how we collect, use and look after your personal data. It further describes your
privacy rights and the control you can exercise in relation to your personal data.
</div>
<h3 className="title-h3">
Important Information
</h3>
<div className="base-text">
This privacy policy is issued on behalf of B BUDDY LTD which is the controller of your personal data. B
BUDDY LTD is a limited liability company registered in the Republic of Cyprus with registration number
HE 417576.
<br />
This privacy policy is addressed to individuals outside B BUDDY LTD with whom we interact, including
individual clients, representatives, directors, shareholders, beneficial owners, coaches and/or
mentors and/or consultants of our application and/or website as well as other users of our services
and suppliers.
</div>
<h3 className="title-h3">What Personal Date We Collect About You?</h3>
<div className="base-text">
We may collect, use, store and transfer different kinds of personal data about you which we have grouped
together as follows:<br />
Identity Data includes first name, maiden name, last name, username or similar identifier, title,
date of birth, gender;<br />
Contact Data includes billing address, delivery address, email address, and telephone numbers
(including mobile telephone number(s)), postal address.<br />
Business Data includes date identifying you in relation to matters on which you instruct us or
in which you are involved such as diplomas and any other educational document or
certificate;<br />
Consent Records Data includes records of any consents you have given us, together with the date,
means of consent and any other information provided such as the subject matter of the consent
provided;<br />
Supplier Data includes contact details and other information about you or your company or
organisation where you provide services to B BUDDY LTD;<br />
Tracker (i.e. any technology - e.g Cookies, unique identifiers, web beacons, embedded scripts,
e-tags and fingerprinting - that enables the tracking of Users, for example by accessing or
storing information on the Users device);<br />
Usage Data (i.e information collected automatically through this Website (or third-party
services employed in this Website), which can include: the IP addresses or domain names of the
computers utilized by the Users who use this Website, the URI addresses (Uniform Resource
Identifier), the time of the request, the method utilized to submit the request to the server,
the size of the file received in response, the numerical code indicating the status of the
server's answer (successful outcome, error, etc.), the country of origin, the features of the
browser and the operating system utilized by the User, the various time details per visit (e.g.,
the time spent on each page within the Application) and the details about the path followed
within the Application with special reference to the sequence of pages visited, and other
parameters about the device operating system and/or the User's IT environment.);<br />
Any other information relating to you which you may provide us with, such as health personal
data, if required by us to be able to provide our services to you.<br />
B BUDDY LTD does not collect any payment details data as they are collected directly by Stripe,
Inc. which is responsible to effect the payments. Stripe, Inc. is a PCI Service Provider Level 1
and it is obliged to follow PCI compliance and the General Data Protection Regulations 2018
(GDPR).
</div>
</>
); );
} };

View File

@ -1,15 +1,55 @@
import React from 'react'; import React from 'react';
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import { useTranslations } from 'next-intl';
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'Bbuddy - Account - Information', title: 'Bbuddy - Account - Notifications',
description: 'Bbuddy desc information' description: 'Bbuddy desc notifications'
}; };
export default function Information() { export default function Notifications() {
const t = useTranslations('Account.Notifications');
return ( return (
<div> <>
Страница Информации о пользователе <ol className="breadcrumb">
</div> <li className="breadcrumb-item active" aria-current="page">{t('title')}</li>
</ol>
<div className="list-notifications ">
<div className="b-notifications primary">
<div className="b-notifications__title">Notification Headline</div>
<div className="b-notifications__text">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc auctor leo eu justo molestie
</div>
<div className="b-notifications__date">25 may 2022</div>
<div className="b-notifications__inner">
<a href="#" className="b-notifications__read">{t('read')}</a>
<a href="#" className="b-notifications__delete">{t('delete')}</a>
</div>
</div>
<div className="b-notifications primary">
<div className="b-notifications__title">Notification Headline</div>
<div className="b-notifications__text">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc auctor leo eu justo molestie
</div>
<div className="b-notifications__date">25 may 2022</div>
<div className="b-notifications__inner">
<a href="#" className="b-notifications__read">{t('read')}</a>
<a href="#" className="b-notifications__delete">{t('delete')}</a>
</div>
</div>
<div className="b-notifications danger">
<div className="b-notifications__title">Notification Headline</div>
<div className="b-notifications__text">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc auctor leo eu justo molestie
</div>
<div className="b-notifications__date">25 may 2022</div>
<div className="b-notifications__inner">
<a href="#" className="b-notifications__read">{t('read')}</a>
<a href="#" className="b-notifications__delete">{t('delete')}</a>
</div>
</div>
</div>
</>
); );
} }

View File

@ -1,15 +1,25 @@
import React from 'react'; import React from 'react';
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import { useTranslations } from 'next-intl';
import { SessionsTabs } from '../../../../components/Account';
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'Bbuddy - Account - Information', title: 'Bbuddy - Account - Sessions',
description: 'Bbuddy desc information' description: 'Bbuddy desc sessions'
}; };
export default function Sessions() { export default function Sessions() {
const t = useTranslations('Account.Sessions');
return ( return (
<div> <SessionsTabs
Страница Информации о пользователе intlConfig={{
</div> upcoming: t('upcoming-sessions'),
requested: t('sessions-requested'),
recent: t('recent-sessions'),
selectTopicLabel: t('topic'),
dateLabel: t('day-start')
}}
/>
); );
} }

View File

@ -0,0 +1,32 @@
import React from 'react';
import { useTranslations } from 'next-intl';
import { Link } from '../../../../../navigation';
export default function ChangePassword({ params }: { params: { userId: string } }) {
const t = useTranslations('Account.Settings');
return (
<>
<ol className="breadcrumb">
<li className="breadcrumb-item">
<Link href={`/${params.userId}/settings` as any}>
{t('title')}
</Link>
</li>
<li className="breadcrumb-item active" aria-current="page">{t('change-password')}</li>
</ol>
<form className="form-settings" action="">
<div className="form-field password-hide">
<input type="text" placeholder={t('old-password')} className="base-input" id="" value="" />
</div>
<div className="form-field password-hide">
<input type="text" placeholder={t('new-password')} className="base-input" id="" value="" />
</div>
<div className="form-field password-show">
<input className="base-input" type="text" placeholder={t('confirm-password')} id="" value="" />
</div>
<button className="btn-apply">{t('save')}</button>
</form>
</>
);
};

View File

@ -1,15 +1,48 @@
import React from 'react'; import React from 'react';
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import { useTranslations } from 'next-intl';
import {Link} from "../../../../navigation";
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'Bbuddy - Account - Information', title: 'Bbuddy - Account - Profile Settings',
description: 'Bbuddy desc information' description: 'Bbuddy desc Profile settings'
}; };
export default function Information() { export default function Settings({ params }: { params: { userId: string } }) {
const t = useTranslations('Account.Settings');
return ( return (
<div> <>
Страница Информации о пользователе <ol className="breadcrumb">
</div> <li className="breadcrumb-item active" aria-current="page">{t('title')}</li>
</ol>
<form className="form-settings" action="">
<div className="user-avatar">
<div className="user-avatar__edit">
<input className="" type="file" id="input-file" />
<label htmlFor="input-file" className="form-label" />
</div>
<div className="user-avatar__text">{t('photo-desc')}</div>
</div>
<div className="form-field">
<input type="text" placeholder={t('name')} className="base-input" id="" value="" />
</div>
<div className="form-field">
<input type="text" placeholder={t('surname')} className="base-input" id="" value="" />
</div>
<div className="form-field date">
<input className="base-input " type="text" placeholder={t('birthday')} id="" value="" />
</div>
<div className="form-field">
<input type="email" placeholder={t('email')} className="base-input" id="" value="" />
</div>
<div className="form-link">
<Link href={`/${params.userId}/settings/change-password` as any}>
{t('change-password')}
</Link>
</div>
<button className="btn-apply">{t('save')}</button>
</form>
</>
); );
} };

View File

@ -1,15 +1,23 @@
import React from 'react'; import React from 'react';
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import {useTranslations} from "next-intl";
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'Bbuddy - Account - Information', title: 'Bbuddy - Account - Help & Support',
description: 'Bbuddy desc information' description: 'Bbuddy desc help & support'
}; };
export default function Information() { export default function Support() {
const t = useTranslations('Account.Support');
return ( return (
<div> <>
Страница Информации о пользователе <ol className="breadcrumb">
</div> <li className="breadcrumb-item active" aria-current="page">{t('title')}</li>
</ol>
<div className="base-text">
some text
</div>
</>
); );
} }

View File

@ -15,7 +15,6 @@ export default function WorkWithUs() {
<ol className="breadcrumb"> <ol className="breadcrumb">
<li className="breadcrumb-item active" aria-current="page">{t('title')}</li> <li className="breadcrumb-item active" aria-current="page">{t('title')}</li>
</ol> </ol>
<div className="b-info"> <div className="b-info">
<div className="image-info"> <div className="image-info">
<img className="" src="/images/info.png" alt="" /> <img className="" src="/images/info.png" alt="" />

View File

@ -2,18 +2,79 @@ import React from 'react';
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';
import { Link } from '../../../../navigation'; import { Link } from '../../../../navigation';
import { getExpertById, getExpertsList } from '../../../../actions/experts';
import {
ExpertCard,
ExpertCertificate,
ExpertInformation,
ExpertPractice
} from '../../../../components/Experts/ExpertDetails';
import { Details } from '../../../../types/experts';
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'Bbuddy - Experts item', title: 'Bbuddy - Experts item',
description: 'Bbuddy desc experts' description: 'Bbuddy desc experts'
}; };
export function generateStaticParams() { export async function generateStaticParams({
return [{ expertId: '1' }, { expertId: '2' }]; params: { locale },
}: { params: { locale: string } }) {
const result: { locale: string, expertId: string }[] = [];
const experts = await getExpertsList({
"themesTagIds": [
1,2,3,4,5,6,7,8
],
"priceFrom": null,
"priceTo": null,
"durationFrom": null,
"durationTo": null
}, locale);
experts?.forEach(({ id }) => {
result.push({ locale, expertId: id.toString() });
});
return result;
} }
export default function ExpertItem({ params }: { params: { expertId: string } }) { export default async function ExpertItem({ params: { expertId = '', locale} }: { params: { expertId: string, locale: string } }) {
if (!params?.expertId) notFound(); if (!expertId) notFound();
const expert = await getExpertById(expertId, locale);
const getAssociationLevel = (accLevelId?: number) => {
if (accLevelId) {
const [cur] = (expert?.associationLevels || []).filter(({ id }) => id === accLevelId) || [];
return cur?.name || '';
}
return '';
};
const getAssociation = (accLevelId?: number) => {
if (accLevelId) {
const [curLevel] = (expert?.associationLevels || []).filter(({ id }) => id === accLevelId) || [];
if (curLevel) {
const [cur] = (expert?.associations || []).filter(({ id }) => id === curLevel.associationId) || [];
return cur?.name || '';
}
}
return '';
};
const generateDescription = ({ id, title, description, document }: Details) => (
<div key={id}>
<h3 className="title-h3">{title}</h3>
<p className="base-text">{description}</p>
{document && (
<div className="sertific">
<ExpertCertificate document={document} />
</div>
)}
</div>
);
return ( return (
<div className="b-page"> <div className="b-page">
@ -24,125 +85,37 @@ export default function ExpertItem({ params }: { params: { expertId: string } })
Back to experts list Back to experts list
</Link> </Link>
</div> </div>
<div className="expert-card"> <ExpertCard expert={expert} />
<div className="expert-card__wrap"> <ExpertInformation expert={expert} locale={locale} />
<div className="expert-card__avatar">
<img src="/images/person.png" className="" alt="" />
</div>
<div className="expert-card__inner">
<h1 className="expert-card__title">{`Matthew Weeks | id ${params.expertId}`}</h1>
<div className="expert-card__info">
<span>12 Practice hours</span>
<i>|</i>
<span>15 Supervision per year</span>
</div>
<div className="expert-card__rating">
<img src="/images/stars.svg" className="" alt="" />
<span>4/5 (out of 345)</span>
</div>
</div>
</div>
<div className="expert-card__wrap-btn">
<a href="#" className="btn-apply">
<img src="/images/calendar-outline.svg" className="" alt="" />
Schedule
</a>
<a href="#" className="btn-video">
<img src="/images/videocam-outline.svg" className="" alt="" />
Video
</a>
</div>
</div>
<h2 className="title-h2">Current Offer</h2>
<div className="skills__list">
<div className="skills__list__item">Engineering &amp; Data</div>
<div className="skills__list__item">Communication</div>
<div className="skills__list__item">Communication</div>
</div>
<p className="base-text">
Hello, my name is Marcelo. I am a Senior UX Designer with more than 6 years of experience working
with the largest companies in the world such as Disney, Globant and currently IBM.
During my career, I have helped organizations solve complex problems using aesthetically pleasing
designs with design while building teams and mentoring other designers.
I can help you: <br />
Prepare for interviews, prepare your resume or CV and work on your LinkedIn profile <br />
Get ready for whiteboard challenges and take-home exercises <br />
Create a great portfolio and case study presentation. <br />
Provide industry information. <br />
Professional orientation <br />
Strategic thinking <br /><br />
Oh, and I also speak Spanish!
</p>
<div className="wrap-btn-prise">
<a href="#" className="btn-apply">
Sign Up Now
</a>
<div className="wrap-btn-prise__text">
45$ <span> / 45min</span>
</div>
</div>
<h2 className="title-h2">Expert Background</h2> <h2 className="title-h2">Expert Background</h2>
<p className="base-text"> <p className="base-text">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, lectus nec viverra Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, lectus nec viverra
malesuada, ligula sem tempor risus, non posuere urna diam a libero. malesuada, ligula sem tempor risus, non posuere urna diam a libero.
</p> </p>
<h3 className="title-h3">Education</h3> {expert?.publicCoachDetails?.educations && expert.publicCoachDetails.educations?.map(generateDescription)}
<p className="base-text"> {expert?.publicCoachDetails?.certificates && expert.publicCoachDetails.certificates.length > 0 && (
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, lectus nec viverra <div>
malesuada, ligula sem tempor risus, non posuere urna diam a libero. <h3 className="title-h3">Professional Certification</h3>
</p> {expert.publicCoachDetails.certificates?.map((cert) => (
<div className="sertific"> <div key={cert.id}>
<img src="/images/sertific.png" className="" alt="" /> <p className="base-text">
</div> {`${getAssociationLevel(cert?.associationLevelId)} ${getAssociation(cert?.associationLevelId)}`}
<h3 className="title-h3">Professional Certification</h3> </p>
<p className="base-text"> {cert.document && (
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, lectus nec viverra <div className="sertific">
malesuada, ligula sem tempor risus, non posuere urna diam a libero. <ExpertCertificate document={cert.document} />
</p> </div>
<h3 className="title-h3">Trainings | Seminars | Courses</h3> )}
<p className="base-text"> </div>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, lectus nec viverra ))}
malesuada, ligula sem tempor risus, non posuere urna diam a libero.
</p>
<h3 className="title-h3">MBA Information</h3>
<p className="base-text">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, lectus nec viverra
malesuada, ligula sem tempor risus, non posuere urna diam a libero.
</p>
<h3 className="title-h3">Managerial Experience</h3>
<p className="base-text">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, lectus nec viverra
malesuada, ligula sem tempor risus, non posuere urna diam a libero.
</p>
<h3 className="title-h3">Entrepreneurial Experience</h3>
<p className="base-text">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, lectus nec viverra
malesuada, ligula sem tempor risus, non posuere urna diam a libero.
</p>
<h3 className="title-h3">Successful Cases From Practice</h3>
<div className="case-list">
<div className="skills__list">
<div className="skills__list__item">Engineering &amp; Data</div>
<div className="skills__list__item">Communication</div>
<div className="skills__list__item">Communication</div>
</div> </div>
<p className="base-text"> )}
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, lectus nec viverra {expert?.publicCoachDetails?.trainings && expert.publicCoachDetails.trainings?.map(generateDescription)}
malesuada, ligula sem tempor risus, non posuere urna diam a libero. {expert?.publicCoachDetails?.mbas && expert.publicCoachDetails.mbas?.map(generateDescription)}
</p> {expert?.publicCoachDetails?.experiences && expert.publicCoachDetails.experiences?.map(generateDescription)}
</div> <ExpertPractice expert={expert} />
<div className="case-list">
<div className="skills__list">
<div className="skills__list__item">Engineering &amp; Data</div>
<div className="skills__list__item">Communication</div>
<div className="skills__list__item">Communication</div>
</div>
<p className="base-text">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, lectus nec viverra
malesuada, ligula sem tempor risus, non posuere urna diam a libero.
</p>
</div>
<h2 className="title-h2">All Offers by this Expert</h2> <h2 className="title-h2">All Offers by this Expert</h2>
<div className="offers-list"> <div className="offers-list">
<div className="card-profile"> <div className="card-profile">

View File

@ -1,14 +1,27 @@
import React from 'react'; import React from 'react';
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import { ExpertsFilter, ExpertsAdditionalFilter } from '../../../components/Experts'; import { getExpertsList } from '../../../actions/experts';
import { ExpertsFilter } from '../../../components/Experts/Filter';
import { ExpertsAdditionalFilter } from '../../../components/Experts/AdditionalFilter';
import { ExpertsList } from '../../../components/Experts/ExpertsList';
import { CustomPagination } from '../../../components/view';
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'Bbuddy - Experts', title: 'Bbuddy - Experts',
description: 'Bbuddy desc experts' description: 'Bbuddy desc experts'
}; };
export default function Experts({ searchParams }: { searchParams: { [key: string]: string | string[] | undefined } }) { export default async function Experts({ params, searchParams }: { params: { locale: string }, searchParams: { [key: string]: string | string[] | undefined } }) {
console.log('search params', searchParams); console.log('search params', searchParams);
const data = await getExpertsList({
"themesTagIds": [
1,2,3,4,5,6,7,8
],
"priceFrom": null,
"priceTo": null,
"durationFrom": null,
"durationTo": null
}, params.locale);
return ( return (
<div className="page-search"> <div className="page-search">
@ -17,162 +30,17 @@ export default function Experts({ searchParams }: { searchParams: { [key: string
<div className="main-find__top"> <div className="main-find__top">
<h2 className="title-h2">Find a expert</h2> <h2 className="title-h2">Find a expert</h2>
<div className="open-filter"> <div className="open-filter">
<img src="/images/options-outline.svg" className="" alt="" /> <img src="/images/options-outline.svg" alt="" />
</div> </div>
</div> </div>
<div className="row"> <div className="row">
<div className="col-xl-3 col-lg-4 d-none d-lg-block"> <div className="col-xl-3 col-lg-4 d-none d-lg-block">
<ExpertsFilter /> <ExpertsFilter locale={params.locale} />
</div> </div>
<div className="col-xl-9 col-lg-8 "> <div className="col-xl-9 col-lg-8 ">
<ExpertsAdditionalFilter /> <ExpertsAdditionalFilter />
<div className="search-result"> <ExpertsList data={data} locale={params.locale} />
<div className="card-profile"> <CustomPagination total={20} />
<div className="card-profile__header">
<div className="card-profile__header__portrait">
<img src="/images/person.png" className="" alt="" />
</div>
<div className="card-profile__header__inner">
<div className="card-profile__header__name">Matthew Weeks</div>
<div className="card-profile__header__price">
45$ <span>/ 45min</span>
</div>
</div>
</div>
<div className="card-profile__skills">
<div className="skills__list">
<div className="skills__list__item">Engineering & Data</div>
<div className="skills__list__item">Engineering & Data</div>
<div className="skills__list__more">+6</div>
</div>
</div>
<div className="card-profile__title">Senior Software Engineer</div>
<div className="card-profile__subtitle">Auth0</div>
<div className="card-profile__desc">
I have worked across a variety of organizations, lead teams, and delivered
quality software for 8 years. In that time I've worked as an independent
consultant, at agencies as a team lead, and as a senior engineer at Auth0. I
also host a podcast https://anchor.fm/work-in-programming where I break down how
</div>
<div className="card-profile__footer">
<a href="#">Details
<img className="" src="/images/chevron-forward.svg" alt="" />
</a>
</div>
</div>
<div className="card-profile">
<div className="card-profile__header">
<div className="card-profile__header__portrait">
<img src="/images/person.png" className="" alt="" />
</div>
<div className="card-profile__header__inner">
<div className="card-profile__header__name">Matthew Weeks</div>
<div className="card-profile__header__price">
45$ <span>/ 45min</span>
</div>
</div>
</div>
<div className="card-profile__skills">
<div className="skills__list">
<div className="skills__list__item">Engineering & Data</div>
<div className="skills__list__item">Engineering & Data</div>
<div className="skills__list__more">+6</div>
</div>
</div>
<div className="card-profile__title">Senior Software Engineer</div>
<div className="card-profile__subtitle">Auth0</div>
<div className="card-profile__desc">
I have worked across a variety of organizations, lead teams, and delivered
quality software for 8 years. In that time I've worked as an independent
consultant, at agencies as a team lead, and as a senior engineer at Auth0. I
also host a podcast https://anchor.fm/work-in-programming where I break down how
</div>
<div className="card-profile__footer">
<a href="#">Details
<img className="" src="/images/chevron-forward.svg" alt="" />
</a>
</div>
</div>
<div className="card-profile">
<div className="card-profile__header">
<div className="card-profile__header__portrait">
<img src="/images/person.png" className="" alt="" />
</div>
<div className="card-profile__header__inner">
<div className="card-profile__header__name">Matthew Weeks</div>
<div className="card-profile__header__price">
45$ <span>/ 45min</span>
</div>
</div>
</div>
<div className="card-profile__skills">
<div className="skills__list">
<div className="skills__list__item">Engineering & Data</div>
<div className="skills__list__item">Engineering & Data</div>
<div className="skills__list__more">+6</div>
</div>
</div>
<div className="card-profile__title">Senior Software Engineer</div>
<div className="card-profile__subtitle">Auth0</div>
<div className="card-profile__desc">
I have worked across a variety of organizations, lead teams, and delivered
quality software for 8 years. In that time I've worked as an independent
consultant, at agencies as a team lead, and as a senior engineer at Auth0. I
also host a podcast https://anchor.fm/work-in-programming where I break down how
</div>
<div className="card-profile__footer">
<a href="#">Details
<img className="" src="/images/chevron-forward.svg" alt="" />
</a>
</div>
</div>
<div className="card-profile">
<div className="card-profile__header">
<div className="card-profile__header__portrait">
<img src="/images/person.png" className="" alt="" />
</div>
<div className="card-profile__header__inner">
<div className="card-profile__header__name">Matthew Weeks</div>
<div className="card-profile__header__price">
45$ <span>/ 45min</span>
</div>
</div>
</div>
<div className="card-profile__skills">
<div className="skills__list">
<div className="skills__list__item">Engineering & Data</div>
<div className="skills__list__item">Engineering & Data</div>
<div className="skills__list__more">+6</div>
</div>
</div>
<div className="card-profile__title">Senior Software Engineer</div>
<div className="card-profile__subtitle">Auth0</div>
<div className="card-profile__desc">
I have worked across a variety of organizations, lead teams, and delivered
quality software for 8 years. In that time I've worked as an independent
consultant, at agencies as a team lead, and as a senior engineer at Auth0. I
also host a podcast https://anchor.fm/work-in-programming where I break down how
</div>
<div className="card-profile__footer">
<a href="#">Details
<img className="" src="/images/chevron-forward.svg" alt="" />
</a>
</div>
</div>
</div>
<ul className="pagination">
<li className="page-item"><a className="page-link" href="#">1</a></li>
<li className="page-item active" aria-current="page">
<a className="page-link" href="#">2</a>
</li>
<li className="page-item"><a className="page-link" href="#">3</a></li>
<li className="page-item"><a className="page-link" href="#">4</a></li>
</ul>
</div> </div>
</div> </div>
</div> </div>

View File

@ -0,0 +1,110 @@
'use client';
import React from 'react';
import { Tabs } from 'antd';
export const SessionsTabs = ({ intlConfig }: { intlConfig: Record<string, string> }) => {
const getChildren = () => (
<>
<div className="filter-session">
<div className="filter-session__item filter-session__item--type">
<select name="" id="">
<option value="">1</option>
<option value="">2</option>
</select>
</div>
</div>
<div className="list-session">
<div className="card-profile">
<div className="card-profile__header">
<div className="card-profile__header__portrait">
<img src="/images/person.png" className="" alt="" />
</div>
<div className="card-profile__header__inner">
<div className="card-profile__header__name">Matthew Weeks</div>
<div className="card-profile__header__title">
Personal Growth Course
</div>
<div className="card-profile__header__date chosen">
Today 10:00 AM - 10:30 AM
</div>
</div>
</div>
</div>
<div className="card-profile">
<div className="card-profile__header">
<div className="card-profile__header__portrait">
<img src="/images/person.png" className="" alt="" />
</div>
<div className="card-profile__header__inner">
<div className="card-profile__header__name">Matthew Weeks</div>
<div className="card-profile__header__title">
Personal Growth Course
</div>
<div className="card-profile__header__date">
8 december at 10:00 AM - 10:30 AM
</div>
</div>
</div>
</div>
<div className="card-profile">
<div className="card-profile__header">
<div className="card-profile__header__portrait">
<img src="/images/person.png" className="" alt="" />
</div>
<div className="card-profile__header__inner">
<div className="card-profile__header__name">Matthew Weeks</div>
<div className="card-profile__header__title">
Personal Growth Course
</div>
<div className="card-profile__header__date">
8 december at 10:00 AM - 10:30 AM
</div>
</div>
</div>
</div>
</div>
</>
);
const tabs = [
{
key: 'upcoming',
label: (
<div className="tabs-session__item active">
{intlConfig?.upcoming || 'Tab 1'}
<span className="count">3</span>
</div>
),
children: getChildren()
},
{
key: 'requested',
label: (
<div className="tabs-session__item">
{intlConfig?.requested || 'Tab 2'}
<span className="count">2</span>
</div>
),
children: getChildren()
},
{
key: 'recent',
label: (
<div className="tabs-session__item">
{intlConfig?.recent || 'Tab 3'}
</div>
),
children: getChildren()
}
];
return (
<Tabs
defaultActiveKey="upcoming"
items={tabs}
renderTabBar={(props, DefaultTabBar) => (<DefaultTabBar {...props} className="tabs-session" />)}
/>
);
};

View File

@ -1 +1,2 @@
export { AccountMenu } from './AccountMenu'; export { AccountMenu } from './AccountMenu';
export { SessionsTabs } from './SessionsTabs';

View File

@ -1,10 +1,11 @@
'use client'; 'use client';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import { Input, Select } from 'antd'; import { Select } from 'antd';
import { AdditionalFilter } from '../../types/experts'; import { AdditionalFilter } from '../../types/experts';
import { INITIAL_ADD_FILTER } from '../../constants/experts'; import { INITIAL_ADD_FILTER } from '../../constants/experts';
import { LOCALES } from '../../constants/locale'; import { LOCALES } from '../../constants/locale';
import { CustomInput } from '../view';
export const ExpertsAdditionalFilter = () => { export const ExpertsAdditionalFilter = () => {
const [filter, setFilter] = useState<AdditionalFilter>(INITIAL_ADD_FILTER); const [filter, setFilter] = useState<AdditionalFilter>(INITIAL_ADD_FILTER);
@ -19,15 +20,20 @@ export const ExpertsAdditionalFilter = () => {
return ( return (
<div className="main-find__search"> <div className="main-find__search">
<div className="main-find__search__input"> <div className="main-find__search__input">
<input className="base-input" type="text" placeholder="Search for an Expert" /> <CustomInput
placeholder="Search for an Expert"
onChange={(e: any) => onChangeFilter('text', e?.target?.value)}
/>
</div> </div>
<Input placeholder="Search for an Expert" onChange={(e) => onChangeFilter('text', e?.target?.value)}/>
<div className="main-find__search__sort"> <div className="main-find__search__sort">
<Select <Select
defaultValue={INITIAL_ADD_FILTER.sort} defaultValue={INITIAL_ADD_FILTER.sort}
onChange={(val) => onChangeFilter('sort', val)} onChange={(val) => onChangeFilter('sort', val)}
options={[ options={[
{ value: 'By top views', label: 'By top views' } { value: 'By top views', label: 'By top views' },
{ value: 'By Price Ascending', label: 'By Price Ascending' },
{ value: 'By Price Descending', label: 'By Price Descending' },
{ value: 'By rating', label: 'By rating' }
]} ]}
/> />
</div> </div>

View File

@ -0,0 +1,144 @@
'use client';
import React, { FC } from 'react';
import Image from 'next/image';
import { Tag, Image as AntdImage, Space } from 'antd';
import { ZoomInOutlined, ZoomOutOutlined, StarFilled } from '@ant-design/icons';
import { ExpertDetails, ExpertDocument } from '../../types/experts';
import { Locale } from '../../types/locale';
import { CustomRate } from '../view/CustomRate';
type ExpertDetailsProps = {
expert: ExpertDetails;
locale?: string;
};
export const ExpertCard: FC<ExpertDetailsProps> = ({ expert }) => {
const { publicCoachDetails } = expert || {};
return (
<div className="expert-card">
<div className="expert-card__wrap">
<div className="expert-card__avatar">
<Image src={publicCoachDetails?.faceImageUrl || '/images/person.png'} width={216} height={216} alt="" />
</div>
<div className="expert-card__inner">
<h1 className="expert-card__title">{`${publicCoachDetails?.name} ${publicCoachDetails?.surname || ''}`}</h1>
<div className="expert-card__info">
<span>{`${publicCoachDetails?.practiceHours} Practice hours`}</span>
<i>|</i>
<span>{`${publicCoachDetails?.supervisionPerYearId} Supervision per year`}</span>
</div>
<div className="expert-card__rating">
<CustomRate defaultValue={4} character={<StarFilled style={{ fontSize: 32 }} />} disabled />
<span>4/5 (out of 345)</span>
</div>
</div>
</div>
<div className="expert-card__wrap-btn">
<a href="#" className="btn-apply">
<img src="/images/calendar-outline.svg" className="" alt="" />
Schedule
</a>
<a href="#" className="btn-video">
<img src="/images/videocam-outline.svg" className="" alt="" />
Video
</a>
</div>
</div>
);
};
export const ExpertInformation: FC<ExpertDetailsProps> = ({ expert, locale }) => {
const { publicCoachDetails: { tags = [], sessionCost = 0, sessionDuration = 0 } } = expert || {};
const isRus = locale === Locale.ru;
return (
<>
<h2 className="title-h2">Current Offer</h2>
<div className="skills__list">
{tags?.map((skill) => <Tag key={skill?.id} className="skills__list__item">{skill?.name}</Tag>)}
</div>
<p className="base-text">
Hello, my name is Marcelo. I am a Senior UX Designer with more than 6 years of experience working
with the largest companies in the world such as Disney, Globant and currently IBM.
During my career, I have helped organizations solve complex problems using aesthetically pleasing
designs with design while building teams and mentoring other designers.
I can help you: <br />
Prepare for interviews, prepare your resume or CV and work on your LinkedIn profile <br />
Get ready for whiteboard challenges and take-home exercises <br />
Create a great portfolio and case study presentation. <br />
Provide industry information. <br />
Professional orientation <br />
Strategic thinking <br /><br />
Oh, and I also speak Spanish!
</p>
<div className="wrap-btn-prise">
<a href="#" className="btn-apply">
Sign Up Now
</a>
<div className="wrap-btn-prise__text">
{`${sessionCost}${isRus ? '₽' : '€'}`} <span>/ {`${sessionDuration}${isRus ? 'мин' : 'min'}`}</span>
</div>
</div>
</>
);
};
export const ExpertPractice: FC<ExpertDetailsProps> = ({ expert }) => {
const { publicCoachDetails: { practiceCases = [], themesGroups = [] } } = expert || {};
return practiceCases?.length > 0 ? (
<div>
<h3 className="title-h3">Successful Cases From Practice</h3>
{practiceCases?.map(({ id, description, themesGroupIds }) => {
const filtered = themesGroups?.filter(({ id }) => themesGroupIds?.includes(+id));
return (
<div key={id} className="case-list">
{themesGroupIds && (
<div className="skills__list">
{filtered?.map(({ id, name }) => (
<div key={id} className="skills__list__item">{name}</div>
))}
</div>
)}
{description && <p className="base-text">{description}</p>}
</div>
)
})}
</div>
) : null;
};
export const ExpertCertificate: FC<{ document: ExpertDocument }> = ({ document }) => (
<AntdImage
width={160}
src={document?.preview?.url}
placeholder={(
<AntdImage
preview={false}
src={document?.preview?.url}
width={160}
fallback="data:image/png;base64,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"
/>
)}
fallback="data:image/png;base64,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"
preview={{
src: document.fullSize?.url,
toolbarRender: (
_,
{
transform: { scale },
actions: { onZoomOut, onZoomIn },
},
) => (
<Space size={12} className="toolbar-wrapper">
<ZoomOutOutlined disabled={scale === 1} onClick={onZoomOut} />
<ZoomInOutlined disabled={scale === 50} onClick={onZoomIn} />
</Space>
)
}}
/>
);

View File

@ -0,0 +1,66 @@
'use client';
import React from 'react';
import { List, Tag } from 'antd';
import { RightOutlined } from '@ant-design/icons';
import Image from 'next/image';
import { Link } from '../../navigation';
import { Locale } from '../../types/locale';
import { ExpertsData } from '../../types/experts';
export const ExpertsList = ({ data, locale }: { data: ExpertsData, locale: string }) => {
const isRus = locale === Locale.ru;
return (
<List
itemLayout="vertical"
size="large"
className="search-result"
dataSource={data}
renderItem={(item) => (
<List.Item key={item?.id} className="card-profile">
<List.Item.Meta
className="card-profile__header"
avatar={(
<div className="card-profile__header__portrait">
<Image src={item?.faceImageUrl || '/images/person.png'} width={100} height={100} alt="" />
</div>
)}
description={(
<div className="card-profile__header__inner">
<Link href={`/experts/${item?.id}` as any}>
<div className="card-profile__header__name">{`${item.name} ${item?.surname || ''}`}</div>
</Link>
<div className="card-profile__header__price">
{`${item?.sessionCost}${isRus ? '₽' : '€'}`} <span>/ {`${item?.sessionDuration}${isRus ? 'мин' : 'min'}`}</span>
</div>
</div>
)}
/>
<div className="card-profile__skills">
<div className="skills__list">
{item?.tags?.slice(0, 2).map((skill) => <Tag key={skill?.id} className="skills__list__item">{skill?.name}</Tag>)}
{item?.tags?.length > 2
? (
<Tag className="skills__list__more">
<Link href={`/experts/${item?.id}` as any}>
{`+${item?.tags?.length - 2}`}
</Link>
</Tag>
) : null}
</div>
</div>
<div className="card-profile__title">{item?.speciality}</div>
<div className="card-profile__subtitle">{item?.specialityDesc}</div>
<div className="card-profile__desc">{item?.description}</div>
<div className="card-profile__footer">
<Link href={`/experts/${item?.id}` as any}>
Details
<RightOutlined style={{ fontSize: '10px', padding: '0 7px' }}/>
</Link>
</div>
</List.Item>
)}
/>
);
};

View File

@ -1,77 +1,11 @@
'use client'; 'use client';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import { List, Switch, Slider } from 'antd'; import { List } from 'antd';
import styled from 'styled-components';
import { FilterType, INITIAL_FILTER } from '../../constants/experts'; import { FilterType, INITIAL_FILTER } from '../../constants/experts';
import { Filter } from '../../types/experts'; import { Filter } from '../../types/experts';
import { CustomSwitch, CustomSlider } from '../view';
const CustomSwitch = styled(Switch)` import {Locale} from "../../types/locale";
height: 24px !important;
background: #F8F8F7 !important;
.ant-switch-handle {
height: 24px !important;
width: 24px !important;
top: 0 !important;
&::before {
box-shadow: none !important;
background: #C4DFE6 !important;
border-radius: 50% !important;
}
}
&.ant-switch-checked {
background: #2c7873 !important;
}
`;
const WrapSlider = styled.div`
width: 100%;
position: relative;
margin: 8px 0 16px -5px;
`;
const CustomSlider = styled(Slider)`
padding-block: 7px !important;
height: 16px !important;
.ant-slider-rail {
background-color: #E5E5E5 !important;
}
.ant-slider-track {
background-color: #66A5AD !important;
}
.ant-slider-handle {
width: 16px !important;
height: 16px !important;
&::before {
width: 16px !important;
height: 16px !important;
inset-inline-start: 0 !important;
inset-block-start: 0 !important;
}
&::after {
width: 16px !important;
height: 16px !important;
background-color: #66A5AD !important;
inset-inline-start: 0 !important;
inset-block-start: 0 !important;
box-shadow: none !important;
}
&:focus, &:hover {
&::after {
box-shadow: 0 0 0 10px rgba(102, 165, 173, .2) !important;
}
}
}
`;
const dataCoaching = [ const dataCoaching = [
{ {
@ -114,8 +48,9 @@ const dataConsultation = [
} }
]; ];
export const ExpertsFilter = () => { export const ExpertsFilter = ({ locale }: { locale: string }) => {
const [filter, setFilter] = useState<Filter>(INITIAL_FILTER); const [filter, setFilter] = useState<Filter>(INITIAL_FILTER);
const isRus = locale === Locale.ru;
const onChangeFilter = useCallback((key: string, value: any) => { const onChangeFilter = useCallback((key: string, value: any) => {
setFilter({ setFilter({
@ -138,7 +73,7 @@ export const ExpertsFilter = () => {
<div className="b-filter__title">{title}</div> <div className="b-filter__title">{title}</div>
<CustomSwitch <CustomSwitch
defaultChecked={filter[key]} defaultChecked={filter[key]}
onChange={(checked) => onChangeFilter(key, checked)} onChange={(checked: boolean) => onChangeFilter(key, checked)}
/> />
</div> </div>
</List.Item> </List.Item>
@ -155,28 +90,28 @@ export const ExpertsFilter = () => {
{getList(dataMentoring)} {getList(dataMentoring)}
<h3 className="title-h3">Business-consultation</h3> <h3 className="title-h3">Business-consultation</h3>
{getList(dataConsultation)} {getList(dataConsultation)}
<h3 className="title-h3">Price from 45 to 170</h3> <h3 className="title-h3">{`Price from 45${isRus ? '₽' : '€'} to 170${isRus ? '₽' : '€'}`}</h3>
<WrapSlider> <div className="b-filter__slider">
<CustomSlider <CustomSlider
range range
step={1} step={1}
defaultValue={INITIAL_FILTER[FilterType.Price]} defaultValue={INITIAL_FILTER[FilterType.Price]}
min={45} min={45}
max={170} max={170}
onChange={(val) => onChangeFilter(FilterType.Price, val)} onChange={(val: any) => onChangeFilter(FilterType.Price, val)}
/> />
</WrapSlider> </div>
<h3 className="title-h3">Duration from 45m to 120m</h3> <h3 className="title-h3">{`Duration from 45${isRus ? 'мин' : 'min'} to 120${isRus ? 'мин' : 'min'}`}</h3>
<WrapSlider> <div className="b-filter__slider">
<CustomSlider <CustomSlider
range range
step={1} step={1}
defaultValue={INITIAL_FILTER[FilterType.Duration]} defaultValue={INITIAL_FILTER[FilterType.Duration]}
min={45} min={45}
max={120} max={120}
onChange={(val) => onChangeFilter(FilterType.Duration, val)} onChange={(val: any) => onChangeFilter(FilterType.Duration, val)}
/> />
</WrapSlider> </div>
<button className="btn-apply">Apply</button> <button className="btn-apply">Apply</button>
</div> </div>
); );

View File

@ -1,2 +0,0 @@
export * from './Filter';
export * from './AdditionalFilter';

View File

@ -0,0 +1,281 @@
'use client';
import React, { FC, useState } from 'react';
import { usePathname } from 'next/navigation';
import Image from 'next/image';
import {Button, Modal as AntdModal, Form, notification} from 'antd';
import { CloseOutlined } from '@ant-design/icons';
import { styled } from 'styled-components';
import { CustomInput } from '../view';
import { getAuth } from '../../actions/auth';
type AuthModalProps = {
open: boolean;
handleCancel: () => void;
mode: 'enter' | 'register' | 'reset' | 'finish';
updateMode: (mode: 'enter' | 'register' | 'reset' | 'finish') => void;
};
const Modal = styled(AntdModal)`
.ant-modal-content {
border-radius: 24px !important;
}
.ant-modal-close {
height: 64px !important;
width: 64px !important;
border-radius: 50% !important;
background: #fff !important;
top: -32px !important;
inset-inline-end: -32px !important;
&:active, &:hover {
background: #fff !important;
}
}
`;
const FilledButton = styled(Button)`
background: #66A5AD !important;
font-size: 15px !important;
border-radius: 8px !important;
height: 54px !important;
box-shadow: 0px 2px 4px 0px rgba(102, 165, 173, 0.32) !important;
`;
const OutlinedButton = styled(Button)`
display: inline-flex !important;
justify-content: center;
align-items: center;
gap: 16px;
border-color: #66A5AD !important;
color: #66A5AD !important;
font-size: 15px !important;
border-radius: 8px !important;
height: 54px !important;
span {
margin-inline-end: 0 !important;
line-height: 15px !important;
}
`;
const LinkButton = styled(Button)`
color: #66A5AD !important;
font-size: 15px !important;
height: auto !important;
padding: 0 !important;
`;
export const AuthModal: FC<AuthModalProps> = ({
open,
handleCancel,
mode,
updateMode
}) => {
const [form] = Form.useForm<{ login: string, password: string, name: string, surname: string, phone: string }>();
const [isLoading, setIsLoading] = useState<boolean>(false);
const paths = usePathname().split('/');
const onAfterClose = () => {
form.resetFields();
};
const onSubmit = () => {
form.validateFields().then(() => {
const { login, password } = form.getFieldsValue();
setIsLoading(true);
getAuth(login, password, paths[1])
.then(({ data }) => {
console.log('jwt', data.jwtToken)
})
.catch((error) => {
notification.error({
message: 'Error',
description: error?.response?.data?.errMessage
});
})
.finally(() => {
setIsLoading(false);
})
});
};
const onValidate = () => {
};
return (
<Modal
open={open}
title={undefined}
onOk={onSubmit}
onCancel={handleCancel}
afterClose={onAfterClose}
footer={false}
width={498}
closeIcon={<CloseOutlined style={{ fontSize: 20, color: '#000' }}/>}
>
<div className="b-modal__auth__content">
<div className="b-modal__auth__logo">
<img className="" src="/images/logo-auth.svg" alt=""/>
</div>
{mode === 'enter' && (
<>
<Form form={form} autoComplete="off" style={{ display: 'flex', gap: 16, flexDirection: 'column' }}>
<Form.Item name="login" rules={[{ required: true }]} noStyle>
<CustomInput
size="small"
placeholder="E-mail"
/>
</Form.Item>
<Form.Item name="password" rules={[{ required: true }]} noStyle>
<CustomInput
size="small"
placeholder="Password"
type="password"
/>
</Form.Item>
</Form>
<FilledButton
type="primary"
onClick={onSubmit}
loading={isLoading}
>
Enter
</FilledButton>
<OutlinedButton onClick={() => updateMode('register')}>Register</OutlinedButton>
<LinkButton
type="link"
onClick={() => updateMode('reset')}
>
Forgot password?
</LinkButton>
<span>or</span>
<OutlinedButton
icon={<Image src="/images/facebook-logo.png" height={20} width={20} alt="" />}
>
Facebook account
</OutlinedButton>
<OutlinedButton
icon={<Image src="/images/apple-logo.png" height={22} width={22} alt="" />}
>
Apple account
</OutlinedButton>
<OutlinedButton
icon={<Image src="/images/google-logo.png" height={20} width={20} alt="" />}
>
Google account
</OutlinedButton>
</>
)}
{mode === 'register' && (
<>
<Form form={form} autoComplete="off" style={{ display: 'flex', gap: 16, flexDirection: 'column' }}>
<Form.Item name="name" noStyle>
<CustomInput
size="small"
placeholder="Name"
/>
</Form.Item>
<Form.Item name="surname" noStyle>
<CustomInput
size="small"
placeholder="Surname"
/>
</Form.Item>
<Form.Item name="phone" noStyle>
<CustomInput
size="small"
placeholder="Phone"
type="phone"
/>
</Form.Item>
<Form.Item name="login" rules={[{ required: true }]} noStyle>
<CustomInput
size="small"
placeholder="E-mail"
/>
</Form.Item>
<Form.Item name="password" rules={[{ required: true }]} noStyle>
<CustomInput
size="small"
placeholder="Password"
type="password"
/>
</Form.Item>
</Form>
<FilledButton
type="primary"
>
Register
</FilledButton>
<span>or</span>
<OutlinedButton
icon={<Image src="/images/facebook-logo.png" height={20} width={20} alt="" />}
>
Facebook account
</OutlinedButton>
<OutlinedButton
icon={<Image src="/images/apple-logo.png" height={22} width={22} alt="" />}
>
Apple account
</OutlinedButton>
<OutlinedButton
icon={<Image src="/images/google-logo.png" height={20} width={20} alt="" />}
>
Google account
</OutlinedButton>
</>
)}
{mode === 'reset' && (
<>
<Form form={form} autoComplete="off" style={{ display: 'flex', gap: 16, flexDirection: 'column' }}>
<Form.Item name="login" rules={[{ required: true }]} noStyle>
<CustomInput
size="small"
placeholder="E-mail"
/>
</Form.Item>
<Form.Item name="password" rules={[{ required: true }]} noStyle>
<CustomInput
size="small"
placeholder="Password"
type="password"
/>
</Form.Item>
</Form>
<FilledButton
type="primary"
>
Reset Password
</FilledButton>
<LinkButton
type="link"
>
Enter
</LinkButton>
</>
)}
{mode === 'finish' && (
<>
<div className="b-modal__auth__agreement">
A link to reset your password has been sent
<br />
to your email
</div>
<FilledButton
type="primary"
>
Enter Account
</FilledButton>
</>
)}
<div className="b-modal__auth__agreement">
I have read and agree with the terms of the
User Agreement, Privacy Policy
</div>
</div>
</Modal>
);
};

View File

@ -1,24 +1,72 @@
import React, { FC } from 'react'; 'use client';
import { useTranslations } from 'next-intl';
import React, { FC, useState, useEffect } from 'react';
import { Button } from 'antd';
import { styled } from 'styled-components';
import { AuthModal } from '../../Modals/AuthModal';
type HeaderAuthLinksProps = { type HeaderAuthLinksProps = {
enterTitle: string;
registerTitle: string;
separatorClass?: string; separatorClass?: string;
}; };
export const HeaderAuthLinks: FC<HeaderAuthLinksProps> = ({ separatorClass = 'b-header__nav__list__line' }) => { const LinkButton = styled(Button)`
const t = useTranslations('Header'); color: #66A5AD !important;
font-size: 16px !important;
height: auto !important;
padding: 0 !important;
font-style: normal !important;
font-weight: 600 !important;
line-height: normal !important;
`;
export const HeaderAuthLinks: FC<HeaderAuthLinksProps> = ({
enterTitle,
registerTitle,
separatorClass = 'b-header__nav__list__line'
}) => {
const [isOpenModal, setIsOpenModal] = useState<boolean>(false);
const [mode, setMode] = useState<'enter' | 'register' | 'reset' | 'finish'>('enter');
useEffect(() => {
if (!isOpenModal) {
setMode('enter');
}
}, [isOpenModal]);
const onOpen = (mode: 'enter' | 'register' | 'reset' | 'finish') => {
setMode(mode);
setIsOpenModal(true);
};
return ( return (
<> <>
<li> <li>
<a href="#">{t('registration')}</a> <LinkButton
type="link"
onClick={() => onOpen('register')}
>
{registerTitle}
</LinkButton>
</li> </li>
<li> <li>
<span className={separatorClass}>|</span> <span className={separatorClass}>|</span>
</li> </li>
<li> <li>
<a href="#">{t('enter')}</a> <LinkButton
type="link"
onClick={() => onOpen('enter')}
>
{enterTitle}
</LinkButton>
</li> </li>
<AuthModal
open={isOpenModal}
handleCancel={() => setIsOpenModal(false)}
mode={mode}
updateMode={setMode}
/>
</> </>
); );
} };

View File

@ -1,12 +1,18 @@
import React, { ReactNode } from 'react'; import React, { ReactNode } from 'react';
import { HeaderAuthLinks } from './HeaderAuthLinks'; import { HeaderAuthLinks } from './HeaderAuthLinks';
export const HeaderMenu = ({ children }: { children: ReactNode }) => ( type HeaderMenuProps = {
children: ReactNode;
enterTitle: string;
registerTitle: string;
};
export const HeaderMenu = ({ children, enterTitle, registerTitle }: HeaderMenuProps) => (
<div className="b-header__nav"> <div className="b-header__nav">
<nav> <nav>
<ul className="b-header__nav__list "> <ul className="b-header__nav__list ">
{children} {children}
<HeaderAuthLinks /> <HeaderAuthLinks enterTitle={enterTitle} registerTitle={registerTitle} />
</ul> </ul>
</nav> </nav>
</div> </div>

View File

@ -5,9 +5,11 @@ import { HeaderAuthLinks } from './HeaderAuthLinks';
type HeaderMenuMobileProps = { type HeaderMenuMobileProps = {
children: ReactNode; children: ReactNode;
enterTitle: string;
registerTitle: string;
}; };
export const HeaderMobileMenu: FC<HeaderMenuMobileProps> = ({ children }) => { export const HeaderMobileMenu: FC<HeaderMenuMobileProps> = ({ children, enterTitle, registerTitle }) => {
const [showMobileMenu, setShowMobileMenu] = useState<boolean>(false); const [showMobileMenu, setShowMobileMenu] = useState<boolean>(false);
return ( return (
@ -27,7 +29,11 @@ export const HeaderMobileMenu: FC<HeaderMenuMobileProps> = ({ children }) => {
<img className="img-default" src="/images/avatar.png" alt=""/> <img className="img-default" src="/images/avatar.png" alt=""/>
</div> </div>
<ul className="menu-mobile__header__nav"> <ul className="menu-mobile__header__nav">
<HeaderAuthLinks separatorClass="menu-mobile__header__nav__line" /> <HeaderAuthLinks
separatorClass="menu-mobile__header__nav__line"
enterTitle={enterTitle}
registerTitle={registerTitle}
/>
</ul> </ul>
</div> </div>
<div className="menu-mobile__body"> <div className="menu-mobile__body">

View File

@ -23,20 +23,20 @@ export const Header: FC<HeaderProps> = ({ locale }) => {
<> <>
<header className="b-header"> <header className="b-header">
<div className="b-inner"> <div className="b-inner">
<div className="b-header__logo"> <Link href={'/' as any} className="b-header__logo">
<img <img
src="/images/logo-header.svg" src="/images/logo-header.svg"
className="img-default" className="img-default"
alt="" alt=""
/> />
</div> </Link>
<HeaderMenu> <HeaderMenu enterTitle={t('enter')} registerTitle={t('registration')}>
{routes} {routes}
</HeaderMenu> </HeaderMenu>
<LanguageSwitcher locale={locale} /> <LanguageSwitcher locale={locale} />
</div> </div>
</header> </header>
<HeaderMobileMenu> <HeaderMobileMenu enterTitle={t('enter')} registerTitle={t('registration')}>
{routes} {routes}
</HeaderMobileMenu> </HeaderMobileMenu>
</> </>

View File

@ -0,0 +1,29 @@
import React from 'react';
import styled from 'styled-components';
import { Input as AntdInput } from 'antd';
const Input = styled(AntdInput)`
padding: 15px 16px !important;
background: #F8F8F7 !important;
border: 1px solid #F8F8F7 !important;
border-radius: 8px !important;
color: #000 !important;
&:focus, &:hover {
border-color: #66A5AD !important;
box-shadow: none !important;
}
&::placeholder {
color: #000 !important;
opacity: .4 !important;
}
&.ant-input-status-error:not(.ant-input-disabled):not(.ant-input-borderless) {
border-color: #ff4d4f !important;
}
`;
export const CustomInput = (props: any) => (
<Input {...props} />
);

View File

@ -0,0 +1,58 @@
'use client';
import React from 'react';
import styled from 'styled-components';
import { Pagination as AntdPagination, PaginationProps } from 'antd';
const DEFAULT_SIZE = 5;
const Pagination = styled(AntdPagination)`
display: flex;
gap: 8px;
.ant-pagination-item {
margin-inline-end: 0 !important;
height: 40px !important;
width: 40px !important;
border-radius: 8px !important;
line-height: 38px !important;
font-family: var(--font-inter) !important;
font-weight: 400 !important;
border-color: #2c7873 !important;
font-size: 16px !important;
a {
color: #2c7873 !important;
}
}
.ant-pagination-item-active {
background: #2c7873 !important;
a {
color: #fff !important;
}
}
.ant-pagination-item:not(.ant-pagination-item-active):active,
.ant-pagination-item:not(.ant-pagination-item-active):hover {
background-color: rgba(44, 120, 115, 0.06) !important;
}
`;
const itemRender: PaginationProps['itemRender'] = (_, type, originalElement) => {
if (type === 'prev' || type === 'next') {
return null;
}
return originalElement;
};
export const CustomPagination = (props: PaginationProps) => (
<Pagination
itemRender={itemRender}
defaultCurrent={1}
defaultPageSize={DEFAULT_SIZE}
{...props}
/>
);

View File

@ -0,0 +1,25 @@
import React from 'react';
import styled from 'styled-components';
import { Rate as AntdRate } from 'antd';
const Rate = styled(AntdRate)`
display: inline-flex !important;
gap: 4px;
li {
margin-inline-end: 0 !important;
padding: 5px 0 0 !important;
&.ant-rate-star-full span {
color: #ffbd00 !important;
}
&.ant-rate-star-zero span {
color: #c4dfe6 !important;
}
}
`;
export const CustomRate = (props: any) => (
<Rate {...props} />
);

View File

View File

@ -0,0 +1,49 @@
'use client';
import React from 'react';
import styled from 'styled-components';
import { Slider as AntdSlider } from 'antd';
const Slider = styled(AntdSlider)`
padding-block: 7px !important;
height: 16px !important;
.ant-slider-rail {
background-color: #E5E5E5 !important;
}
.ant-slider-track {
background-color: #66A5AD !important;
}
.ant-slider-handle {
width: 16px !important;
height: 16px !important;
&::before {
width: 16px !important;
height: 16px !important;
inset-inline-start: 0 !important;
inset-block-start: 0 !important;
}
&::after {
width: 16px !important;
height: 16px !important;
background-color: #66A5AD !important;
inset-inline-start: 0 !important;
inset-block-start: 0 !important;
box-shadow: none !important;
}
&:focus, &:hover {
&::after {
box-shadow: 0 0 0 10px rgba(102, 165, 173, .2) !important;
}
}
}
`;
export const CustomSlider = (props: any) => (
<Slider {...props} />
);

View File

@ -0,0 +1,34 @@
'use client';
import React from 'react';
import styled from 'styled-components';
import { Switch as AntdSwitch } from 'antd';
const Switch = styled(AntdSwitch)`
height: 24px !important;
background: #F8F8F7 !important;
.ant-switch-handle {
height: 24px !important;
width: 24px !important;
top: 0 !important;
&::before {
box-shadow: none !important;
background: #C4DFE6 !important;
border-radius: 50% !important;
}
}
&.ant-switch-checked {
background: #F8F8F7 !important;
.ant-switch-handle::before {
background: #66A5AD !important;
}
}
`;
export const CustomSwitch = (props: any) => (
<Switch {...props} />
);

View File

@ -0,0 +1,7 @@
'use client';
export * from './CustomSwitch';
export * from './CustomSlider';
export * from './CustomPagination';
export * from './CustomRate';
export * from './CustomInput';

1
src/constants/common.ts Normal file
View File

@ -0,0 +1 @@
export const BASE_URL = process.env.NEXT_PUBLIC_SERVER_BASE_URL || 'https://api.bbuddy.expert/api';

33
src/lib/apiClient.ts Normal file
View File

@ -0,0 +1,33 @@
import axios, { InternalAxiosRequestConfig, AxiosResponse } from 'axios';
import { BASE_URL } from '../constants/common';
// import { getAuthToken } from '../../utils';
export const onSuccessRequestCallback = (config: InternalAxiosRequestConfig) => {
const newConfig = { ...config };
// if (!newConfig.headers) {
// newConfig.headers = {};
// }
//
// if (IS_DEV && !newConfig.headers.Authorization && getAuthToken()) {
// newConfig.headers.Authorization = `Bearer ${getAuthToken()}`;
// }
newConfig.headers['Content-Type'] = 'application/json';
return newConfig;
};
export const onSuccessResponseCallback = (response: AxiosResponse) => response;
export const onErrorResponseCallback = (error: any) => Promise.reject(error);
export const apiClient = axios.create({
baseURL: BASE_URL
});
apiClient.interceptors.request.use(onSuccessRequestCallback);
apiClient.interceptors.response.use(
onSuccessResponseCallback,
onErrorResponseCallback
);

View File

@ -9,4 +9,5 @@ export default createMiddleware({
export const config = { export const config = {
matcher: ['/', '/(en|ru|de|it|es|fr)/:path*'] matcher: ['/', '/(en|ru|de|it|es|fr)/:path*']
// matcher: ['/', '/(en|ru|de|it|es|fr).html']
}; };

View File

@ -0,0 +1,29 @@
.b {
&-modal {
&__auth {
&__content {
padding: 80px 76px 68px;
display: flex;
flex-direction: column;
gap: 16px;
text-align: center;
span, div {
font-size: 13px;
}
& > span {
color: #66A5AD;
}
}
&__logo {
margin-bottom: 16px;
}
&__agreement {
}
}
}
}

View File

@ -10,6 +10,10 @@ body{
background-color: #ffffff; background-color: #ffffff;
} }
*::selection {
background-color: #2C7873;
color: #fff;
}
.b-wrapper { .b-wrapper {
width: 100%; width: 100%;
@ -305,6 +309,12 @@ body{
gap: 16px; gap: 16px;
} }
&__slider {
width: 100%;
position: relative;
margin: 8px 0 16px -5px;
}
&__title { &__title {
color: $black; color: $black;
@include rem(15); @include rem(15);
@ -480,18 +490,20 @@ body{
// //
.card-profile { .card-profile {
display: flex; display: flex !important;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px !important;
padding-bottom: 8px; padding: 0 0 8px !important;
border-bottom: 1px solid #C4DFE6; border-block-end: 1px solid #C4DFE6 !important;
margin: 0 0 16px !important;
&__header { &__header {
display: flex; display: flex;
padding-bottom: 8px; padding-bottom: 8px;
align-items: center; align-items: center !important;
gap: 16px; gap: 16px;
align-self: stretch; align-self: stretch;
margin-block-end: 0 !important;
&__portrait { &__portrait {
width: 86px; width: 86px;
@ -499,8 +511,9 @@ body{
border-radius: 16px; border-radius: 16px;
border: 2px solid #FFF; border: 2px solid #FFF;
background: lightgray 50%; background: lightgray 50%;
box-shadow: 0px 8px 16px 0px rgba(102, 165, 173, 0.32); box-shadow: 0 8px 16px 0 rgba(102, 165, 173, 0.32);
overflow: hidden; overflow: hidden;
margin-right: -16px;
img { img {
object-fit: cover; object-fit: cover;
@ -599,70 +612,29 @@ body{
align-self: stretch; align-self: stretch;
&__item { &__item {
height: 22px; margin-inline-end: 0 !important;
border-radius: 4px; padding-inline: 4px !important;
background: #2C7873; font-size: 0.75rem;
display: inline-flex;
padding: 4px;
color: #FFF;
@include rem(12);
font-style: normal; font-style: normal;
font-weight: 300; font-weight: 300;
line-height: 116.667%; line-height: 20px;
align-items: center; background: #2C7873 !important;
border-color: #2C7873 !important;
color: #fff !important;
height: 22px;
text-transform: capitalize;
} }
&__more { &__more {
height: 22px; margin-inline-end: 0 !important;
display: inline-flex; padding-inline: 4px !important;
align-items: center; font-size: 0.75rem;
padding: 4px;
border-radius: 4px;
border: 1px solid #2C7873;
color: #2C7873;
@include rem(12);
font-style: normal; font-style: normal;
font-weight: 300; font-weight: 300;
line-height: 116.667%; line-height: 22px;
} border-color: #2C7873 !important;
} color: #2C7873 !important;
height: 22px;
.pagination {
display: flex;
gap: 8px;
.page-item {
border-radius: 8px;
display: inline-flex;
height: 40px;
width: 40px;
.page-link {
border-radius: 8px;
border: 1px solid #2C7873;
display: inline-flex;
padding: 8px 16px;
justify-content: center;
align-items: center;
background: $white;
height: 40px;
width: 40px;
color: #2C7873;
text-align: center;
font-family: 'Inter', sans-serif;
@include rem(16);
font-style: normal;
font-weight: 400;
line-height: 150%;
text-decoration: none;
}
&.active {
.page-link {
background: #2C7873;
color: $white;
}
}
} }
} }
@ -740,17 +712,17 @@ body{
&__rating { &__rating {
display: flex; display: flex;
align-items: center;
gap: 8px; gap: 8px;
flex-flow: wrap; flex-flow: wrap;
span { & > span {
display: block; display: block;
color: #2C7873; color: #2C7873;
@include rem(13); @include rem(13);
font-style: normal; font-style: normal;
font-weight: 500; font-weight: 500;
line-height: 123.077%; line-height: 123.077%;
padding-top: 9px;
} }
img { img {

View File

@ -610,10 +610,3 @@
} }
} }
} }
.search-result {
display: flex;
flex-direction: column;
gap: 16px;
margin-bottom: 16px;
}

View File

@ -9,5 +9,6 @@
@import "_pages.scss"; @import "_pages.scss";
@import "_form.scss"; @import "_form.scss";
@import "_message.scss"; @import "_message.scss";
@import "_auth-modal.scss";

View File

@ -1,7 +1,108 @@
export type Filter = Record<string, any>; export type Filter = Record<string, any>;
// export type Filter = {
// themesTagIds: number[];
// priceFrom?: number | null;
// priceTo?: number | null;
// durationFrom?: number | null;
// durationTo?: number | null;
// };
export type AdditionalFilter = { export type AdditionalFilter = {
text?: string; text?: string;
sort?: string; sort?: string;
language?: string[]; language?: string[];
}; };
export type Tag = {
id: number;
groupId: number;
name: string
couchCount?: number;
group?: string | null;
};
export type File = {
id: number;
fileType: string;
url: string;
};
export interface ExpertDocument {
fileName: string;
original?: File;
preview?: File;
fullSize?: File;
}
export type Details = {
id: number;
userId?:number;
title?: string;
description?: string;
document?: ExpertDocument;
};
export type Certificate = {
id: number;
userId?: number;
associationLevelId?: number;
document?: ExpertDocument;
};
export type Practice = {
id: number;
userId?: number;
description?: string;
themesGroupIds?: number[];
};
export type ThemeGroup = {
id: number;
name: string;
isActive?: boolean;
canDeleted?: boolean;
};
export type Association = {
id: number;
name: string;
};
export type AssociationLevel = {
id: number;
associationId: number;
name: string;
};
export interface ExpertItem {
id: number;
name: string;
surname?: string;
faceImageUrl?: string;
sessionDuration: number;
sessionCost: number;
coachRating?: number;
tags: Tag[];
speciality?: string;
specialityDesc?: string;
description?: string;
}
export type ExpertsData = ExpertItem[];
export type ExpertDetails = {
publicCoachDetails: ExpertItem & {
practiceHours?: number;
supervisionPerYearId?: number;
educations?: Details[];
certificates?: Certificate[];
trainings?: Details[];
mbas?: Details[];
experiences?: Details[];
practiceCases?: Practice[];
themesGroups?: ThemeGroup[];
};
associations?: Association[];
associationLevels?: AssociationLevel[];
};

View File

@ -29,7 +29,8 @@
"**/*.ts", "**/*.ts",
"**/*.tsx", "**/*.tsx",
".next/types/**/*.ts", ".next/types/**/*.ts",
"dist/types/**/*.ts" "dist/types/**/*.ts",
"build/types/**/*.ts"
], ],
"exclude": [ "exclude": [
"node_modules" "node_modules"