From fa171dfe6ca79172053800b39501109aa4011e4c Mon Sep 17 00:00:00 2001
From: balibabu <cike8899@users.noreply.github.com>
Date: Fri, 8 Mar 2024 17:09:07 +0800
Subject: [PATCH] feat: submit new password to backend and submit user
 information and add Form to UserSettingProfile (#114)

* feat: add Form to UserSettingProfile

* feat: submit user information

* feat: submit new password to backend
---
 web/src/constants/setting.ts                  | 421 ++++++++++++++++++
 web/src/hooks/userSettingHook.ts              |  13 +
 .../knowledge-setting/configuration.tsx       |  29 +-
 web/src/pages/setting/model.ts                |   3 +-
 .../components/setting-title/index.tsx        |  19 +
 web/src/pages/user-setting/hooks.ts           |  23 +
 web/src/pages/user-setting/index.less         |  12 +
 web/src/pages/user-setting/index.tsx          |   8 +-
 .../user-setting/setting-password/index.less  |   3 +
 .../user-setting/setting-password/index.tsx   | 136 +++++-
 .../user-setting/setting-profile/index.less   |   7 +
 .../user-setting/setting-profile/index.tsx    | 191 +++++++-
 web/src/pages/user-setting/sidebar/index.less |   3 +
 web/src/pages/user-setting/sidebar/index.tsx  |  17 +-
 web/src/utils/fileUtil.ts                     |  27 ++
 15 files changed, 880 insertions(+), 32 deletions(-)
 create mode 100644 web/src/pages/user-setting/components/setting-title/index.tsx
 create mode 100644 web/src/pages/user-setting/hooks.ts
 create mode 100644 web/src/pages/user-setting/index.less
 create mode 100644 web/src/pages/user-setting/setting-password/index.less
 create mode 100644 web/src/pages/user-setting/setting-profile/index.less
 create mode 100644 web/src/pages/user-setting/sidebar/index.less

diff --git a/web/src/constants/setting.ts b/web/src/constants/setting.ts
index 915a2d6..7a60477 100644
--- a/web/src/constants/setting.ts
+++ b/web/src/constants/setting.ts
@@ -15,3 +15,424 @@ export const UserSettingRouteMap = {
   [UserSettingRouteKey.Team]: 'Team',
   [UserSettingRouteKey.Logout]: 'Log out',
 };
+
+export const TimezoneList = [
+  'UTC-11\tPacific/Midway',
+  'UTC-11\tPacific/Niue',
+  'UTC-11\tPacific/Pago_Pago',
+  'UTC-10\tAmerica/Adak',
+  'UTC-10\tPacific/Honolulu',
+  'UTC-10\tPacific/Rarotonga',
+  'UTC-10\tPacific/Tahiti',
+  'UTC-9:30\tPacific/Marquesas',
+  'UTC-9\tAmerica/Anchorage',
+  'UTC-9\tAmerica/Juneau',
+  'UTC-9\tAmerica/Metlakatla',
+  'UTC-9\tAmerica/Nome',
+  'UTC-9\tAmerica/Sitka',
+  'UTC-9\tAmerica/Yakutat',
+  'UTC-9\tPacific/Gambier',
+  'UTC-8\tAmerica/Los_Angeles',
+  'UTC-8\tAmerica/Tijuana',
+  'UTC-8\tAmerica/Vancouver',
+  'UTC-8\tPacific/Pitcairn',
+  'UTC-7\tAmerica/Boise',
+  'UTC-7\tAmerica/Cambridge_Bay',
+  'UTC-7\tAmerica/Ciudad_Juarez',
+  'UTC-7\tAmerica/Creston',
+  'UTC-7\tAmerica/Dawson',
+  'UTC-7\tAmerica/Dawson_Creek',
+  'UTC-7\tAmerica/Denver',
+  'UTC-7\tAmerica/Edmonton',
+  'UTC-7\tAmerica/Fort_Nelson',
+  'UTC-7\tAmerica/Hermosillo',
+  'UTC-7\tAmerica/Inuvik',
+  'UTC-7\tAmerica/Mazatlan',
+  'UTC-7\tAmerica/Phoenix',
+  'UTC-7\tAmerica/Whitehorse',
+  'UTC-7\tAmerica/Yellowknife',
+  'UTC-6\tAmerica/Bahia_Banderas',
+  'UTC-6\tAmerica/Belize',
+  'UTC-6\tAmerica/Chicago',
+  'UTC-6\tAmerica/Chihuahua',
+  'UTC-6\tAmerica/Costa_Rica',
+  'UTC-6\tAmerica/El_Salvador',
+  'UTC-6\tAmerica/Guatemala',
+  'UTC-6\tAmerica/Indiana/Knox',
+  'UTC-6\tAmerica/Indiana/Tell_City',
+  'UTC-6\tAmerica/Managua',
+  'UTC-6\tAmerica/Matamoros',
+  'UTC-6\tAmerica/Menominee',
+  'UTC-6\tAmerica/Merida',
+  'UTC-6\tAmerica/Mexico_City',
+  'UTC-6\tAmerica/Monterrey',
+  'UTC-6\tAmerica/North_Dakota/Beulah',
+  'UTC-6\tAmerica/North_Dakota/Center',
+  'UTC-6\tAmerica/North_Dakota/New_Salem',
+  'UTC-6\tAmerica/Ojinaga',
+  'UTC-6\tAmerica/Rankin_Inlet',
+  'UTC-6\tAmerica/Regina',
+  'UTC-6\tAmerica/Resolute',
+  'UTC-6\tAmerica/Swift_Current',
+  'UTC-6\tAmerica/Tegucigalpa',
+  'UTC-6\tAmerica/Winnipeg',
+  'UTC-6\tPacific/Easter',
+  'UTC-6\tPacific/Galapagos',
+  'UTC-5\tAmerica/Atikokan',
+  'UTC-5\tAmerica/Bogota',
+  'UTC-5\tAmerica/Cancun',
+  'UTC-5\tAmerica/Cayman',
+  'UTC-5\tAmerica/Detroit',
+  'UTC-5\tAmerica/Eirunepe',
+  'UTC-5\tAmerica/Grand_Turk',
+  'UTC-5\tAmerica/Guayaquil',
+  'UTC-5\tAmerica/Havana',
+  'UTC-5\tAmerica/Indiana/Indianapolis',
+  'UTC-5\tAmerica/Indiana/Marengo',
+  'UTC-5\tAmerica/Indiana/Petersburg',
+  'UTC-5\tAmerica/Indiana/Vevay',
+  'UTC-5\tAmerica/Indiana/Vincennes',
+  'UTC-5\tAmerica/Indiana/Winamac',
+  'UTC-5\tAmerica/Iqaluit',
+  'UTC-5\tAmerica/Jamaica',
+  'UTC-5\tAmerica/Kentucky/Louisville',
+  'UTC-5\tAmerica/Kentucky/Monticello',
+  'UTC-5\tAmerica/Lima',
+  'UTC-5\tAmerica/Nassau',
+  'UTC-5\tAmerica/New_York',
+  'UTC-5\tAmerica/Panama',
+  'UTC-5\tAmerica/Port-au-Prince',
+  'UTC-5\tAmerica/Rio_Branco',
+  'UTC-5\tAmerica/Toronto',
+  'UTC-4\tAmerica/Anguilla',
+  'UTC-4\tAmerica/Antigua',
+  'UTC-4\tAmerica/Aruba',
+  'UTC-4\tAmerica/Asuncion',
+  'UTC-4\tAmerica/Barbados',
+  'UTC-4\tAmerica/Blanc-Sablon',
+  'UTC-4\tAmerica/Boa_Vista',
+  'UTC-4\tAmerica/Campo_Grande',
+  'UTC-4\tAmerica/Caracas',
+  'UTC-4\tAmerica/Cuiaba',
+  'UTC-4\tAmerica/Curacao',
+  'UTC-4\tAmerica/Dominica',
+  'UTC-4\tAmerica/Glace_Bay',
+  'UTC-4\tAmerica/Goose_Bay',
+  'UTC-4\tAmerica/Grenada',
+  'UTC-4\tAmerica/Guadeloupe',
+  'UTC-4\tAmerica/Guyana',
+  'UTC-4\tAmerica/Halifax',
+  'UTC-4\tAmerica/Kralendijk',
+  'UTC-4\tAmerica/La_Paz',
+  'UTC-4\tAmerica/Lower_Princes',
+  'UTC-4\tAmerica/Manaus',
+  'UTC-4\tAmerica/Marigot',
+  'UTC-4\tAmerica/Martinique',
+  'UTC-4\tAmerica/Moncton',
+  'UTC-4\tAmerica/Montserrat',
+  'UTC-4\tAmerica/Porto_Velho',
+  'UTC-4\tAmerica/Port_of_Spain',
+  'UTC-4\tAmerica/Puerto_Rico',
+  'UTC-4\tAmerica/Santiago',
+  'UTC-4\tAmerica/Santo_Domingo',
+  'UTC-4\tAmerica/St_Barthelemy',
+  'UTC-4\tAmerica/St_Kitts',
+  'UTC-4\tAmerica/St_Lucia',
+  'UTC-4\tAmerica/St_Thomas',
+  'UTC-4\tAmerica/St_Vincent',
+  'UTC-4\tAmerica/Thule',
+  'UTC-4\tAmerica/Tortola',
+  'UTC-4\tAtlantic/Bermuda',
+  'UTC-3:30\tAmerica/St_Johns',
+  'UTC-3\tAmerica/Araguaina',
+  'UTC-3\tAmerica/Argentina/Buenos_Aires',
+  'UTC-3\tAmerica/Argentina/Catamarca',
+  'UTC-3\tAmerica/Argentina/Cordoba',
+  'UTC-3\tAmerica/Argentina/Jujuy',
+  'UTC-3\tAmerica/Argentina/La_Rioja',
+  'UTC-3\tAmerica/Argentina/Mendoza',
+  'UTC-3\tAmerica/Argentina/Rio_Gallegos',
+  'UTC-3\tAmerica/Argentina/Salta',
+  'UTC-3\tAmerica/Argentina/San_Juan',
+  'UTC-3\tAmerica/Argentina/San_Luis',
+  'UTC-3\tAmerica/Argentina/Tucuman',
+  'UTC-3\tAmerica/Argentina/Ushuaia',
+  'UTC-3\tAmerica/Bahia',
+  'UTC-3\tAmerica/Belem',
+  'UTC-3\tAmerica/Cayenne',
+  'UTC-3\tAmerica/Fortaleza',
+  'UTC-3\tAmerica/Maceio',
+  'UTC-3\tAmerica/Miquelon',
+  'UTC-3\tAmerica/Montevideo',
+  'UTC-3\tAmerica/Paramaribo',
+  'UTC-3\tAmerica/Punta_Arenas',
+  'UTC-3\tAmerica/Recife',
+  'UTC-3\tAmerica/Santarem',
+  'UTC-3\tAmerica/Sao_Paulo',
+  'UTC-3\tAntarctica/Palmer',
+  'UTC-3\tAntarctica/Rothera',
+  'UTC-3\tAtlantic/Stanley',
+  'UTC-2\tAmerica/Noronha',
+  'UTC-2\tAmerica/Nuuk',
+  'UTC-2\tAtlantic/South_Georgia',
+  'UTC-1\tAmerica/Scoresbysund',
+  'UTC-1\tAtlantic/Azores',
+  'UTC-1\tAtlantic/Cape_Verde',
+  'UTC+0\tAfrica/Abidjan',
+  'UTC+0\tAfrica/Accra',
+  'UTC+0\tAfrica/Bamako',
+  'UTC+0\tAfrica/Banjul',
+  'UTC+0\tAfrica/Bissau',
+  'UTC+0\tAfrica/Casablanca',
+  'UTC+0\tAfrica/Conakry',
+  'UTC+0\tAfrica/Dakar',
+  'UTC+0\tAfrica/El_Aaiun',
+  'UTC+0\tAfrica/Freetown',
+  'UTC+0\tAfrica/Lome',
+  'UTC+0\tAfrica/Monrovia',
+  'UTC+0\tAfrica/Nouakchott',
+  'UTC+0\tAfrica/Ouagadougou',
+  'UTC+0\tAfrica/Sao_Tome',
+  'UTC+0\tAmerica/Danmarkshavn',
+  'UTC+0\tAntarctica/Troll',
+  'UTC+0\tAtlantic/Canary',
+  'UTC+0\tAtlantic/Faroe',
+  'UTC+0\tAtlantic/Madeira',
+  'UTC+0\tAtlantic/Reykjavik',
+  'UTC+0\tAtlantic/St_Helena',
+  'UTC+0\tEurope/Dublin',
+  'UTC+0\tEurope/Guernsey',
+  'UTC+0\tEurope/Isle_of_Man',
+  'UTC+0\tEurope/Jersey',
+  'UTC+0\tEurope/Lisbon',
+  'UTC+0\tEurope/London',
+  'UTC+1\tAfrica/Algiers',
+  'UTC+1\tAfrica/Bangui',
+  'UTC+1\tAfrica/Brazzaville',
+  'UTC+1\tAfrica/Ceuta',
+  'UTC+1\tAfrica/Douala',
+  'UTC+1\tAfrica/Kinshasa',
+  'UTC+1\tAfrica/Lagos',
+  'UTC+1\tAfrica/Libreville',
+  'UTC+1\tAfrica/Luanda',
+  'UTC+1\tAfrica/Malabo',
+  'UTC+1\tAfrica/Ndjamena',
+  'UTC+1\tAfrica/Niamey',
+  'UTC+1\tAfrica/Porto-Novo',
+  'UTC+1\tAfrica/Tunis',
+  'UTC+1\tAfrica/Windhoek',
+  'UTC+1\tArctic/Longyearbyen',
+  'UTC+1\tEurope/Amsterdam',
+  'UTC+1\tEurope/Andorra',
+  'UTC+1\tEurope/Belgrade',
+  'UTC+1\tEurope/Berlin',
+  'UTC+1\tEurope/Bratislava',
+  'UTC+1\tEurope/Brussels',
+  'UTC+1\tEurope/Budapest',
+  'UTC+1\tEurope/Copenhagen',
+  'UTC+1\tEurope/Gibraltar',
+  'UTC+1\tEurope/Ljubljana',
+  'UTC+1\tEurope/Luxembourg',
+  'UTC+1\tEurope/Madrid',
+  'UTC+1\tEurope/Malta',
+  'UTC+1\tEurope/Monaco',
+  'UTC+1\tEurope/Oslo',
+  'UTC+1\tEurope/Paris',
+  'UTC+1\tEurope/Podgorica',
+  'UTC+1\tEurope/Prague',
+  'UTC+1\tEurope/Rome',
+  'UTC+1\tEurope/San_Marino',
+  'UTC+1\tEurope/Sarajevo',
+  'UTC+1\tEurope/Skopje',
+  'UTC+1\tEurope/Stockholm',
+  'UTC+1\tEurope/Tirane',
+  'UTC+1\tEurope/Vaduz',
+  'UTC+1\tEurope/Vatican',
+  'UTC+1\tEurope/Vienna',
+  'UTC+1\tEurope/Warsaw',
+  'UTC+1\tEurope/Zagreb',
+  'UTC+1\tEurope/Zurich',
+  'UTC+2\tAfrica/Blantyre',
+  'UTC+2\tAfrica/Bujumbura',
+  'UTC+2\tAfrica/Cairo',
+  'UTC+2\tAfrica/Gaborone',
+  'UTC+2\tAfrica/Harare',
+  'UTC+2\tAfrica/Johannesburg',
+  'UTC+2\tAfrica/Juba',
+  'UTC+2\tAfrica/Khartoum',
+  'UTC+2\tAfrica/Kigali',
+  'UTC+2\tAfrica/Lubumbashi',
+  'UTC+2\tAfrica/Lusaka',
+  'UTC+2\tAfrica/Maputo',
+  'UTC+2\tAfrica/Maseru',
+  'UTC+2\tAfrica/Mbabane',
+  'UTC+2\tAfrica/Tripoli',
+  'UTC+2\tAsia/Beirut',
+  'UTC+2\tAsia/Famagusta',
+  'UTC+2\tAsia/Gaza',
+  'UTC+2\tAsia/Hebron',
+  'UTC+2\tAsia/Jerusalem',
+  'UTC+2\tAsia/Nicosia',
+  'UTC+2\tEurope/Athens',
+  'UTC+2\tEurope/Bucharest',
+  'UTC+2\tEurope/Chisinau',
+  'UTC+2\tEurope/Helsinki',
+  'UTC+2\tEurope/Kaliningrad',
+  'UTC+2\tEurope/Kyiv',
+  'UTC+2\tEurope/Mariehamn',
+  'UTC+2\tEurope/Riga',
+  'UTC+2\tEurope/Sofia',
+  'UTC+2\tEurope/Tallinn',
+  'UTC+2\tEurope/Vilnius',
+  'UTC+3\tAfrica/Addis_Ababa',
+  'UTC+3\tAfrica/Asmara',
+  'UTC+3\tAfrica/Dar_es_Salaam',
+  'UTC+3\tAfrica/Djibouti',
+  'UTC+3\tAfrica/Kampala',
+  'UTC+3\tAfrica/Mogadishu',
+  'UTC+3\tAfrica/Nairobi',
+  'UTC+3\tAntarctica/Syowa',
+  'UTC+3\tAsia/Aden',
+  'UTC+3\tAsia/Amman',
+  'UTC+3\tAsia/Baghdad',
+  'UTC+3\tAsia/Bahrain',
+  'UTC+3\tAsia/Damascus',
+  'UTC+3\tAsia/Kuwait',
+  'UTC+3\tAsia/Qatar',
+  'UTC+3\tAsia/Riyadh',
+  'UTC+3\tEurope/Istanbul',
+  'UTC+3\tEurope/Kirov',
+  'UTC+3\tEurope/Minsk',
+  'UTC+3\tEurope/Moscow',
+  'UTC+3\tEurope/Simferopol',
+  'UTC+3\tEurope/Volgograd',
+  'UTC+3\tIndian/Antananarivo',
+  'UTC+3\tIndian/Comoro',
+  'UTC+3\tIndian/Mayotte',
+  'UTC+3:30\tAsia/Tehran',
+  'UTC+4\tAsia/Baku',
+  'UTC+4\tAsia/Dubai',
+  'UTC+4\tAsia/Muscat',
+  'UTC+4\tAsia/Tbilisi',
+  'UTC+4\tAsia/Yerevan',
+  'UTC+4\tEurope/Astrakhan',
+  'UTC+4\tEurope/Samara',
+  'UTC+4\tEurope/Saratov',
+  'UTC+4\tEurope/Ulyanovsk',
+  'UTC+4\tIndian/Mahe',
+  'UTC+4\tIndian/Mauritius',
+  'UTC+4\tIndian/Reunion',
+  'UTC+4:30\tAsia/Kabul',
+  'UTC+5\tAntarctica/Mawson',
+  'UTC+5\tAsia/Aqtau',
+  'UTC+5\tAsia/Aqtobe',
+  'UTC+5\tAsia/Ashgabat',
+  'UTC+5\tAsia/Atyrau',
+  'UTC+5\tAsia/Dushanbe',
+  'UTC+5\tAsia/Karachi',
+  'UTC+5\tAsia/Oral',
+  'UTC+5\tAsia/Qyzylorda',
+  'UTC+5\tAsia/Samarkand',
+  'UTC+5\tAsia/Tashkent',
+  'UTC+5\tAsia/Yekaterinburg',
+  'UTC+5\tIndian/Kerguelen',
+  'UTC+5\tIndian/Maldives',
+  'UTC+5:30\tAsia/Colombo',
+  'UTC+5:30\tAsia/Kolkata',
+  'UTC+5:45\tAsia/Kathmandu',
+  'UTC+6\tAntarctica/Vostok',
+  'UTC+6\tAsia/Almaty',
+  'UTC+6\tAsia/Bishkek',
+  'UTC+6\tAsia/Dhaka',
+  'UTC+6\tAsia/Omsk',
+  'UTC+6\tAsia/Qostanay',
+  'UTC+6\tAsia/Thimphu',
+  'UTC+6\tAsia/Urumqi',
+  'UTC+6\tIndian/Chagos',
+  'UTC+6:30\tAsia/Yangon',
+  'UTC+6:30\tIndian/Cocos',
+  'UTC+7\tAntarctica/Davis',
+  'UTC+7\tAsia/Bangkok',
+  'UTC+7\tAsia/Barnaul',
+  'UTC+7\tAsia/Hovd',
+  'UTC+7\tAsia/Ho_Chi_Minh',
+  'UTC+7\tAsia/Jakarta',
+  'UTC+7\tAsia/Krasnoyarsk',
+  'UTC+7\tAsia/Novokuznetsk',
+  'UTC+7\tAsia/Novosibirsk',
+  'UTC+7\tAsia/Phnom_Penh',
+  'UTC+7\tAsia/Pontianak',
+  'UTC+7\tAsia/Tomsk',
+  'UTC+7\tAsia/Vientiane',
+  'UTC+7\tIndian/Christmas',
+  'UTC+8\tAsia/Brunei',
+  'UTC+8\tAsia/Choibalsan',
+  'UTC+8\tAsia/Hong_Kong',
+  'UTC+8\tAsia/Irkutsk',
+  'UTC+8\tAsia/Kuala_Lumpur',
+  'UTC+8\tAsia/Kuching',
+  'UTC+8\tAsia/Macau',
+  'UTC+8\tAsia/Makassar',
+  'UTC+8\tAsia/Manila',
+  'UTC+8\tAsia/Shanghai',
+  'UTC+8\tAsia/Singapore',
+  'UTC+8\tAsia/Taipei',
+  'UTC+8\tAsia/Ulaanbaatar',
+  'UTC+8\tAustralia/Perth',
+  'UTC+8:45\tAustralia/Eucla',
+  'UTC+9\tAsia/Chita',
+  'UTC+9\tAsia/Dili',
+  'UTC+9\tAsia/Jayapura',
+  'UTC+9\tAsia/Khandyga',
+  'UTC+9\tAsia/Pyongyang',
+  'UTC+9\tAsia/Seoul',
+  'UTC+9\tAsia/Tokyo',
+  'UTC+9\tAsia/Yakutsk',
+  'UTC+9\tPacific/Palau',
+  'UTC+9:30\tAustralia/Adelaide',
+  'UTC+9:30\tAustralia/Broken_Hill',
+  'UTC+9:30\tAustralia/Darwin',
+  'UTC+10\tAntarctica/DumontDUrville',
+  'UTC+10\tAntarctica/Macquarie',
+  'UTC+10\tAsia/Ust-Nera',
+  'UTC+10\tAsia/Vladivostok',
+  'UTC+10\tAustralia/Brisbane',
+  'UTC+10\tAustralia/Hobart',
+  'UTC+10\tAustralia/Lindeman',
+  'UTC+10\tAustralia/Melbourne',
+  'UTC+10\tAustralia/Sydney',
+  'UTC+10\tPacific/Chuuk',
+  'UTC+10\tPacific/Guam',
+  'UTC+10\tPacific/Port_Moresby',
+  'UTC+10\tPacific/Saipan',
+  'UTC+10:30\tAustralia/Lord_Howe',
+  'UTC+11\tAntarctica/Casey',
+  'UTC+11\tAsia/Magadan',
+  'UTC+11\tAsia/Sakhalin',
+  'UTC+11\tAsia/Srednekolymsk',
+  'UTC+11\tPacific/Bougainville',
+  'UTC+11\tPacific/Efate',
+  'UTC+11\tPacific/Guadalcanal',
+  'UTC+11\tPacific/Kosrae',
+  'UTC+11\tPacific/Norfolk',
+  'UTC+11\tPacific/Noumea',
+  'UTC+11\tPacific/Pohnpei',
+  'UTC+12\tAntarctica/McMurdo',
+  'UTC+12\tAsia/Anadyr',
+  'UTC+12\tAsia/Kamchatka',
+  'UTC+12\tPacific/Auckland',
+  'UTC+12\tPacific/Fiji',
+  'UTC+12\tPacific/Funafuti',
+  'UTC+12\tPacific/Kwajalein',
+  'UTC+12\tPacific/Majuro',
+  'UTC+12\tPacific/Nauru',
+  'UTC+12\tPacific/Tarawa',
+  'UTC+12\tPacific/Wake',
+  'UTC+12\tPacific/Wallis',
+  'UTC+12:45\tPacific/Chatham',
+  'UTC+13\tPacific/Apia',
+  'UTC+13\tPacific/Fakaofo',
+  'UTC+13\tPacific/Kanton',
+  'UTC+13\tPacific/Tongatapu',
+  'UTC+14\tPacific/Kiritimati',
+];
diff --git a/web/src/hooks/userSettingHook.ts b/web/src/hooks/userSettingHook.ts
index 2bba749..0c13beb 100644
--- a/web/src/hooks/userSettingHook.ts
+++ b/web/src/hooks/userSettingHook.ts
@@ -74,3 +74,16 @@ export const useLogout = () => {
 
   return logout;
 };
+
+export const useSaveSetting = () => {
+  const dispatch = useDispatch();
+
+  const saveSetting = useCallback(
+    (userInfo: { new_password: string } | IUserInfo): number => {
+      return dispatch<any>({ type: 'settingModel/setting', payload: userInfo });
+    },
+    [dispatch],
+  );
+
+  return saveSetting;
+};
diff --git a/web/src/pages/add-knowledge/components/knowledge-setting/configuration.tsx b/web/src/pages/add-knowledge/components/knowledge-setting/configuration.tsx
index 67a7438..f678c44 100644
--- a/web/src/pages/add-knowledge/components/knowledge-setting/configuration.tsx
+++ b/web/src/pages/add-knowledge/components/knowledge-setting/configuration.tsx
@@ -26,6 +26,11 @@ import { useDispatch, useSelector } from 'umi';
 import { useFetchLlmList, useSelectLlmOptions } from '@/hooks/llmHooks';
 import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
 import { IKnowledge } from '@/interfaces/database/knowledge';
+import {
+  getBase64FromUploadFileList,
+  getUploadFileListFromBase64,
+  normFile,
+} from '@/utils/fileUtil';
 import { PlusOutlined } from '@ant-design/icons';
 import { LlmModelType } from '../../constant';
 import styles from './index.less';
@@ -43,26 +48,12 @@ const Configuration = () => {
     (state: any) => state.kSModel.knowledgeDetails,
   );
 
-  const normFile = (e: any) => {
-    if (Array.isArray(e)) {
-      return e;
-    }
-    return e?.fileList;
-  };
-
   const parserList = useSelectParserList();
 
   const embeddingModelOptions = useSelectLlmOptions();
 
   const onFinish = async (values: any) => {
-    console.info(values);
-    const fileList = values.avatar;
-    let avatar;
-
-    if (Array.isArray(fileList) && fileList.length > 0) {
-      avatar = fileList[0].thumbUrl;
-    }
-
+    const avatar = getBase64FromUploadFileList(values.avatar);
     dispatch({
       type: 'kSModel/updateKb',
       payload: {
@@ -78,12 +69,10 @@ const Configuration = () => {
   };
 
   useEffect(() => {
-    const avatar = knowledgeDetails.avatar;
-    let fileList: UploadFile[] = [];
+    const fileList: UploadFile[] = getUploadFileListFromBase64(
+      knowledgeDetails.avatar,
+    );
 
-    if (avatar) {
-      fileList = [{ uid: '1', name: 'file', thumbUrl: avatar, status: 'done' }];
-    }
     form.setFieldsValue({
       ...pick(knowledgeDetails, [
         'description',
diff --git a/web/src/pages/setting/model.ts b/web/src/pages/setting/model.ts
index ce46dc8..381380d 100644
--- a/web/src/pages/setting/model.ts
+++ b/web/src/pages/setting/model.ts
@@ -52,7 +52,7 @@ const model: DvaModel<SettingModelState> = {
       const { data } = yield call(userService.setting, payload);
       const { retcode } = data;
       if (retcode === 0) {
-        message.success('密码修改成功!');
+        message.success('Modified!');
         yield put({
           type: 'updateState',
           payload: {
@@ -61,7 +61,6 @@ const model: DvaModel<SettingModelState> = {
         });
         yield put({
           type: 'getUserInfo',
-          payload: {},
         });
       }
     },
diff --git a/web/src/pages/user-setting/components/setting-title/index.tsx b/web/src/pages/user-setting/components/setting-title/index.tsx
new file mode 100644
index 0000000..1996d50
--- /dev/null
+++ b/web/src/pages/user-setting/components/setting-title/index.tsx
@@ -0,0 +1,19 @@
+import { Typography } from 'antd';
+
+const { Title, Paragraph } = Typography;
+
+interface IProps {
+  title: string;
+  description: string;
+}
+
+const SettingTitle = ({ title, description }: IProps) => {
+  return (
+    <div>
+      <Title level={5}>{title}</Title>
+      <Paragraph>{description}</Paragraph>
+    </div>
+  );
+};
+
+export default SettingTitle;
diff --git a/web/src/pages/user-setting/hooks.ts b/web/src/pages/user-setting/hooks.ts
new file mode 100644
index 0000000..ee670c6
--- /dev/null
+++ b/web/src/pages/user-setting/hooks.ts
@@ -0,0 +1,23 @@
+import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
+import { Form } from 'antd';
+import { useEffect, useState } from 'react';
+
+export const useValidateSubmittable = () => {
+  const [form] = Form.useForm();
+  const [submittable, setSubmittable] = useState<boolean>(false);
+
+  // Watch all values
+  const values = Form.useWatch([], form);
+
+  useEffect(() => {
+    form
+      .validateFields({ validateOnly: true })
+      .then(() => setSubmittable(true))
+      .catch(() => setSubmittable(false));
+  }, [form, values]);
+
+  return { submittable, form };
+};
+
+export const useGetUserInfoLoading = () =>
+  useOneNamespaceEffectsLoading('settingModel', ['setting']);
diff --git a/web/src/pages/user-setting/index.less b/web/src/pages/user-setting/index.less
new file mode 100644
index 0000000..2332db1
--- /dev/null
+++ b/web/src/pages/user-setting/index.less
@@ -0,0 +1,12 @@
+.settingWrapper {
+  width: 100%;
+
+  .outletWrapper {
+    padding: 32px 32px 0;
+  }
+
+  .itemDescription {
+    padding: 10px 0;
+    margin: 0;
+  }
+}
diff --git a/web/src/pages/user-setting/index.tsx b/web/src/pages/user-setting/index.tsx
index ba364de..e23c4e4 100644
--- a/web/src/pages/user-setting/index.tsx
+++ b/web/src/pages/user-setting/index.tsx
@@ -2,11 +2,15 @@ import { Flex } from 'antd';
 import { Outlet } from 'umi';
 import SideBar from './sidebar';
 
+import styles from './index.less';
+
 const UserSetting = () => {
   return (
-    <Flex>
+    <Flex className={styles.settingWrapper}>
       <SideBar></SideBar>
-      <Outlet></Outlet>
+      <Flex flex={1} className={styles.outletWrapper}>
+        <Outlet></Outlet>
+      </Flex>
     </Flex>
   );
 };
diff --git a/web/src/pages/user-setting/setting-password/index.less b/web/src/pages/user-setting/setting-password/index.less
new file mode 100644
index 0000000..a3adeaa
--- /dev/null
+++ b/web/src/pages/user-setting/setting-password/index.less
@@ -0,0 +1,3 @@
+.passwordWrapper {
+  width: 100%;
+}
diff --git a/web/src/pages/user-setting/setting-password/index.tsx b/web/src/pages/user-setting/setting-password/index.tsx
index 02ce07f..919af72 100644
--- a/web/src/pages/user-setting/setting-password/index.tsx
+++ b/web/src/pages/user-setting/setting-password/index.tsx
@@ -1,5 +1,139 @@
+import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
+import { useSaveSetting } from '@/hooks/userSettingHook';
+import { rsaPsw } from '@/utils';
+import { Button, Divider, Form, Input, Space } from 'antd';
+import SettingTitle from '../components/setting-title';
+import { useValidateSubmittable } from '../hooks';
+
+import parentStyles from '../index.less';
+import styles from './index.less';
+
+type FieldType = {
+  password?: string;
+  new_password?: string;
+  confirm_password?: string;
+};
+
+const tailLayout = {
+  wrapperCol: { offset: 20, span: 4 },
+};
+
 const UserSettingPassword = () => {
-  return <div>UserSettingPassword</div>;
+  const loading = useOneNamespaceEffectsLoading('settingModel', ['setting']);
+  const { form, submittable } = useValidateSubmittable();
+  const saveSetting = useSaveSetting();
+
+  const onFinish = (values: any) => {
+    const password = rsaPsw(values.password) as string;
+    const new_password = rsaPsw(values.new_password) as string;
+
+    saveSetting({ password, new_password });
+  };
+
+  const onFinishFailed = (errorInfo: any) => {
+    console.log('Failed:', errorInfo);
+  };
+
+  return (
+    <section className={styles.passwordWrapper}>
+      <SettingTitle
+        title="Password"
+        description="Please enter your current password to change your password."
+      ></SettingTitle>
+      <Divider />
+      <Form
+        colon={false}
+        name="basic"
+        labelAlign={'left'}
+        labelCol={{ span: 8 }}
+        wrapperCol={{ span: 16 }}
+        style={{ width: '100%' }}
+        initialValues={{ remember: true }}
+        onFinish={onFinish}
+        onFinishFailed={onFinishFailed}
+        form={form}
+        autoComplete="off"
+        // requiredMark={'optional'}
+      >
+        <Form.Item<FieldType>
+          label="Current password"
+          name="password"
+          rules={[
+            {
+              required: true,
+              message: 'Please input your password!',
+              whitespace: true,
+            },
+          ]}
+        >
+          <Input.Password />
+        </Form.Item>
+        <Divider />
+        <Form.Item label="New password" required>
+          <Form.Item<FieldType>
+            noStyle
+            name="new_password"
+            rules={[
+              {
+                required: true,
+                message: 'Please input your password!',
+                whitespace: true,
+              },
+            ]}
+          >
+            <Input.Password />
+          </Form.Item>
+          <p className={parentStyles.itemDescription}>
+            Your new password must be more than 8 characters.
+          </p>
+        </Form.Item>
+        <Divider />
+        <Form.Item<FieldType>
+          label="Confirm new password"
+          name="confirm_password"
+          dependencies={['new_password']}
+          rules={[
+            {
+              required: true,
+              message: 'Please confirm your password!',
+              whitespace: true,
+            },
+            ({ getFieldValue }) => ({
+              validator(_, value) {
+                if (!value || getFieldValue('new_password') === value) {
+                  return Promise.resolve();
+                }
+                return Promise.reject(
+                  new Error('The new password that you entered do not match!'),
+                );
+              },
+            }),
+          ]}
+        >
+          <Input.Password />
+        </Form.Item>
+        <Divider />
+        <Form.Item
+          {...tailLayout}
+          shouldUpdate={(prevValues, curValues) =>
+            prevValues.additional !== curValues.additional
+          }
+        >
+          <Space>
+            <Button htmlType="button">Cancel</Button>
+            <Button
+              type="primary"
+              htmlType="submit"
+              disabled={!submittable}
+              loading={loading}
+            >
+              Save
+            </Button>
+          </Space>
+        </Form.Item>
+      </Form>
+    </section>
+  );
 };
 
 export default UserSettingPassword;
diff --git a/web/src/pages/user-setting/setting-profile/index.less b/web/src/pages/user-setting/setting-profile/index.less
new file mode 100644
index 0000000..1950f8c
--- /dev/null
+++ b/web/src/pages/user-setting/setting-profile/index.less
@@ -0,0 +1,7 @@
+.profileWrapper {
+  width: 100%;
+  .emailDescription {
+    padding: 10px 0;
+    margin: 0;
+  }
+}
diff --git a/web/src/pages/user-setting/setting-profile/index.tsx b/web/src/pages/user-setting/setting-profile/index.tsx
index 10df96d..29f4be2 100644
--- a/web/src/pages/user-setting/setting-profile/index.tsx
+++ b/web/src/pages/user-setting/setting-profile/index.tsx
@@ -1,5 +1,194 @@
+import { useSaveSetting, useSelectUserInfo } from '@/hooks/userSettingHook';
+import {
+  getBase64FromUploadFileList,
+  getUploadFileListFromBase64,
+  normFile,
+} from '@/utils/fileUtil';
+import { PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons';
+import {
+  Button,
+  Divider,
+  Form,
+  Input,
+  Select,
+  Space,
+  Tooltip,
+  Upload,
+  UploadFile,
+} from 'antd';
+import { useEffect } from 'react';
+import SettingTitle from '../components/setting-title';
+import { TimezoneList } from '../constants';
+import { useGetUserInfoLoading, useValidateSubmittable } from '../hooks';
+
+import parentStyles from '../index.less';
+import styles from './index.less';
+
+const { Option } = Select;
+
+type FieldType = {
+  nickname?: string;
+  language?: string;
+  email?: string;
+  color_schema?: string;
+  timezone?: string;
+  avatar?: string;
+};
+
+const tailLayout = {
+  wrapperCol: { offset: 20, span: 4 },
+};
+
 const UserSettingProfile = () => {
-  return <div>UserSettingProfile</div>;
+  const userInfo = useSelectUserInfo();
+  const saveSetting = useSaveSetting();
+  const loading = useGetUserInfoLoading();
+  const { form, submittable } = useValidateSubmittable();
+
+  const onFinish = (values: any) => {
+    const avatar = getBase64FromUploadFileList(values.avatar);
+    saveSetting({ ...values, avatar });
+  };
+
+  const onFinishFailed = (errorInfo: any) => {
+    console.log('Failed:', errorInfo);
+  };
+
+  useEffect(() => {
+    const fileList: UploadFile[] = getUploadFileListFromBase64(userInfo.avatar);
+    form.setFieldsValue({ ...userInfo, avatar: fileList });
+  }, [form, userInfo]);
+
+  return (
+    <section className={styles.profileWrapper}>
+      <SettingTitle
+        title="Profile"
+        description="Update your photo and personal details here."
+      ></SettingTitle>
+      <Divider />
+      <Form
+        colon={false}
+        name="basic"
+        labelAlign={'left'}
+        labelCol={{ span: 8 }}
+        wrapperCol={{ span: 16 }}
+        style={{ width: '100%' }}
+        initialValues={{ remember: true }}
+        onFinish={onFinish}
+        onFinishFailed={onFinishFailed}
+        form={form}
+        autoComplete="off"
+      >
+        <Form.Item<FieldType>
+          label="Username"
+          name="nickname"
+          rules={[
+            {
+              required: true,
+              message: 'Please input your username!',
+              whitespace: true,
+            },
+          ]}
+        >
+          <Input />
+        </Form.Item>
+        <Divider />
+        <Form.Item<FieldType>
+          label={
+            <div>
+              <Space>
+                Your photo
+                <Tooltip title="prompt text">
+                  <QuestionCircleOutlined />
+                </Tooltip>
+              </Space>
+              <div>This will be displayed on your profile.</div>
+            </div>
+          }
+          name="avatar"
+          valuePropName="fileList"
+          getValueFromEvent={normFile}
+        >
+          <Upload
+            listType="picture-card"
+            maxCount={1}
+            showUploadList={{ showPreviewIcon: false, showRemoveIcon: false }}
+          >
+            <button style={{ border: 0, background: 'none' }} type="button">
+              <PlusOutlined />
+              <div style={{ marginTop: 8 }}>Upload</div>
+            </button>
+          </Upload>
+        </Form.Item>
+        <Divider />
+        <Form.Item<FieldType>
+          label="Color schema"
+          name="color_schema"
+          rules={[
+            { required: true, message: 'Please select your color schema!' },
+          ]}
+        >
+          <Select placeholder="select your color schema">
+            <Option value="Bright">Bright</Option>
+            <Option value="Dark">Dark</Option>
+          </Select>
+        </Form.Item>
+        <Divider />
+        <Form.Item<FieldType>
+          label="Language"
+          name="language"
+          rules={[{ required: true, message: 'Please input your language!' }]}
+        >
+          <Select placeholder="select your language">
+            <Option value="English">English</Option>
+            <Option value="Chinese">Chinese</Option>
+          </Select>
+        </Form.Item>
+        <Divider />
+        <Form.Item<FieldType>
+          label="Timezone"
+          name="timezone"
+          rules={[{ required: true, message: 'Please input your timezone!' }]}
+        >
+          <Select placeholder="select your timezone" showSearch>
+            {TimezoneList.map((x) => (
+              <Option value={x} key={x}>
+                {x}
+              </Option>
+            ))}
+          </Select>
+        </Form.Item>
+        <Divider />
+        <Form.Item label="Email address">
+          <Form.Item<FieldType> name="email" noStyle>
+            <Input disabled />
+          </Form.Item>
+          <p className={parentStyles.itemDescription}>
+            Once registered, an account cannot be changed and can only be
+            cancelled.
+          </p>
+        </Form.Item>
+        <Form.Item
+          {...tailLayout}
+          shouldUpdate={(prevValues, curValues) =>
+            prevValues.additional !== curValues.additional
+          }
+        >
+          <Space>
+            <Button htmlType="button">Cancel</Button>
+            <Button
+              type="primary"
+              htmlType="submit"
+              disabled={!submittable}
+              loading={loading}
+            >
+              Save
+            </Button>
+          </Space>
+        </Form.Item>
+      </Form>
+    </section>
+  );
 };
 
 export default UserSettingProfile;
diff --git a/web/src/pages/user-setting/sidebar/index.less b/web/src/pages/user-setting/sidebar/index.less
new file mode 100644
index 0000000..a72d58c
--- /dev/null
+++ b/web/src/pages/user-setting/sidebar/index.less
@@ -0,0 +1,3 @@
+.sideBarWrapper {
+  padding-top: 32px;
+}
diff --git a/web/src/pages/user-setting/sidebar/index.tsx b/web/src/pages/user-setting/sidebar/index.tsx
index ed62b9c..2abd82a 100644
--- a/web/src/pages/user-setting/sidebar/index.tsx
+++ b/web/src/pages/user-setting/sidebar/index.tsx
@@ -10,6 +10,8 @@ import {
   UserSettingRouteMap,
 } from '../constants';
 
+import styles from './index.less';
+
 type MenuItem = Required<MenuProps>['items'][number];
 
 function getItem(
@@ -45,12 +47,15 @@ const SideBar = () => {
   }, [pathName]);
 
   return (
-    <Menu
-      selectedKeys={selectedKeys}
-      mode="inline"
-      items={items}
-      onClick={handleMenuClick}
-    />
+    <section className={styles.sideBarWrapper}>
+      <Menu
+        selectedKeys={selectedKeys}
+        mode="inline"
+        items={items}
+        onClick={handleMenuClick}
+        style={{ width: 312 }}
+      />
+    </section>
   );
 };
 
diff --git a/web/src/utils/fileUtil.ts b/web/src/utils/fileUtil.ts
index ab9cced..537abb4 100644
--- a/web/src/utils/fileUtil.ts
+++ b/web/src/utils/fileUtil.ts
@@ -1,3 +1,5 @@
+import { UploadFile } from 'antd';
+
 export const transformFile2Base64 = (val: any): Promise<any> => {
   return new Promise((resolve, reject) => {
     const reader = new FileReader();
@@ -26,3 +28,28 @@ export const transformBase64ToFile = (
   }
   return new File([u8arr], filename, { type: mimeType });
 };
+
+export const normFile = (e: any) => {
+  if (Array.isArray(e)) {
+    return e;
+  }
+  return e?.fileList;
+};
+
+export const getUploadFileListFromBase64 = (avatar: string) => {
+  let fileList: UploadFile[] = [];
+
+  if (avatar) {
+    fileList = [{ uid: '1', name: 'file', thumbUrl: avatar, status: 'done' }];
+  }
+
+  return fileList;
+};
+
+export const getBase64FromUploadFileList = (fileList?: UploadFile[]) => {
+  if (Array.isArray(fileList) && fileList.length > 0) {
+    return fileList[0].thumbUrl;
+  }
+
+  return '';
+};
-- 
GitLab