From bdbe83104265b2dc7a888ca337ac1bdcc1f0315f Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Wed, 18 Jun 2025 17:08:14 +0000 Subject: [PATCH 1/2] Society Community portal --- 502.html | 2 +- README.md | 2 +- app-shell/src/_schema.json | 3 +- backend/README.md | 6 +- backend/package.json | 4 +- backend/src/config.js | 4 +- backend/src/db/api/chatmessages.js | 370 +++++++++++++ backend/src/db/api/chatroomparticipants.js | 334 ++++++++++++ backend/src/db/api/chatrooms.js | 297 +++++++++++ backend/src/db/api/users.js | 10 + backend/src/db/db.config.js | 2 +- backend/src/db/migrations/1750265371768.js | 72 +++ backend/src/db/migrations/1750265419400.js | 47 ++ backend/src/db/migrations/1750265455323.js | 49 ++ backend/src/db/migrations/1750265515961.js | 72 +++ backend/src/db/migrations/1750265548741.js | 49 ++ backend/src/db/migrations/1750265578751.js | 49 ++ backend/src/db/migrations/1750265604945.js | 49 ++ backend/src/db/migrations/1750265634411.js | 72 +++ backend/src/db/migrations/1750265662159.js | 54 ++ backend/src/db/migrations/1750265689235.js | 54 ++ backend/src/db/migrations/1750265715124.js | 54 ++ backend/src/db/migrations/1750265741614.js | 54 ++ backend/src/db/models/chatmessages.js | 69 +++ backend/src/db/models/chatroomparticipants.js | 61 +++ backend/src/db/models/chatrooms.js | 75 +++ backend/src/db/models/users.js | 16 + .../db/seeders/20200430130760-user-roles.js | 78 +++ .../db/seeders/20231127130745-sample-data.js | 388 ++++++++++++++ backend/src/db/seeders/20250618164931.js | 87 ++++ backend/src/db/seeders/20250618165155.js | 87 ++++ backend/src/db/seeders/20250618165354.js | 87 ++++ backend/src/index.js | 28 +- backend/src/routes/chatmessages.js | 442 ++++++++++++++++ backend/src/routes/chatroomparticipants.js | 445 ++++++++++++++++ backend/src/routes/chatrooms.js | 439 ++++++++++++++++ backend/src/services/chatmessages.js | 117 +++++ backend/src/services/chatroomparticipants.js | 118 +++++ backend/src/services/chatrooms.js | 114 ++++ backend/src/services/notifications/list.js | 2 +- backend/src/services/search.js | 4 + docker/docker-compose.yml | 2 +- frontend/README.md | 2 +- frontend/src/components/AsideMenuLayer.tsx | 2 +- .../Chatmessages/CardChatmessages.tsx | 142 +++++ .../Chatmessages/ListChatmessages.tsx | 108 ++++ .../Chatmessages/TableChatmessages.tsx | 484 +++++++++++++++++ .../configureChatmessagesCols.tsx | 130 +++++ .../CardChatroomparticipants.tsx | 121 +++++ .../ListChatroomparticipants.tsx | 99 ++++ .../TableChatroomparticipants.tsx | 486 ++++++++++++++++++ .../configureChatroomparticipantsCols.tsx | 105 ++++ .../components/Chatrooms/CardChatrooms.tsx | 123 +++++ .../components/Chatrooms/ListChatrooms.tsx | 99 ++++ .../components/Chatrooms/TableChatrooms.tsx | 484 +++++++++++++++++ .../Chatrooms/configureChatroomsCols.tsx | 105 ++++ .../components/WebPageComponents/Footer.tsx | 2 +- frontend/src/helpers/dataFormatter.js | 19 + frontend/src/menuAside.ts | 24 + frontend/src/pages/_app.tsx | 4 +- .../pages/chatmessages/[chatmessagesId].tsx | 173 +++++++ .../pages/chatmessages/chatmessages-edit.tsx | 171 ++++++ .../pages/chatmessages/chatmessages-list.tsx | 173 +++++++ .../pages/chatmessages/chatmessages-new.tsx | 132 +++++ .../pages/chatmessages/chatmessages-table.tsx | 172 +++++++ .../pages/chatmessages/chatmessages-view.tsx | 116 +++++ .../[chatroomparticipantsId].tsx | 155 ++++++ .../chatroomparticipants-edit.tsx | 155 ++++++ .../chatroomparticipants-list.tsx | 171 ++++++ .../chatroomparticipants-new.tsx | 120 +++++ .../chatroomparticipants-table.tsx | 170 ++++++ .../chatroomparticipants-view.tsx | 94 ++++ .../src/pages/chatrooms/[chatroomsId].tsx | 157 ++++++ .../src/pages/chatrooms/chatrooms-edit.tsx | 155 ++++++ .../src/pages/chatrooms/chatrooms-list.tsx | 168 ++++++ .../src/pages/chatrooms/chatrooms-new.tsx | 118 +++++ .../src/pages/chatrooms/chatrooms-table.tsx | 167 ++++++ .../src/pages/chatrooms/chatrooms-view.tsx | 189 +++++++ frontend/src/pages/dashboard.tsx | 106 ++++ frontend/src/pages/index.tsx | 18 +- frontend/src/pages/login.tsx | 2 +- frontend/src/pages/privacy-policy.tsx | 2 +- frontend/src/pages/terms-of-use.tsx | 2 +- frontend/src/pages/users/users-view.tsx | 76 +++ frontend/src/pages/web_pages/about.tsx | 16 +- frontend/src/pages/web_pages/contact.tsx | 12 +- frontend/src/pages/web_pages/faq.tsx | 10 +- frontend/src/pages/web_pages/home.tsx | 16 +- frontend/src/pages/web_pages/services.tsx | 12 +- .../stores/chatmessages/chatmessagesSlice.ts | 241 +++++++++ .../chatroomparticipantsSlice.ts | 254 +++++++++ .../src/stores/chatrooms/chatroomsSlice.ts | 236 +++++++++ frontend/src/stores/store.ts | 6 + 93 files changed, 10607 insertions(+), 65 deletions(-) create mode 100644 backend/src/db/api/chatmessages.js create mode 100644 backend/src/db/api/chatroomparticipants.js create mode 100644 backend/src/db/api/chatrooms.js create mode 100644 backend/src/db/migrations/1750265371768.js create mode 100644 backend/src/db/migrations/1750265419400.js create mode 100644 backend/src/db/migrations/1750265455323.js create mode 100644 backend/src/db/migrations/1750265515961.js create mode 100644 backend/src/db/migrations/1750265548741.js create mode 100644 backend/src/db/migrations/1750265578751.js create mode 100644 backend/src/db/migrations/1750265604945.js create mode 100644 backend/src/db/migrations/1750265634411.js create mode 100644 backend/src/db/migrations/1750265662159.js create mode 100644 backend/src/db/migrations/1750265689235.js create mode 100644 backend/src/db/migrations/1750265715124.js create mode 100644 backend/src/db/migrations/1750265741614.js create mode 100644 backend/src/db/models/chatmessages.js create mode 100644 backend/src/db/models/chatroomparticipants.js create mode 100644 backend/src/db/models/chatrooms.js create mode 100644 backend/src/db/seeders/20250618164931.js create mode 100644 backend/src/db/seeders/20250618165155.js create mode 100644 backend/src/db/seeders/20250618165354.js create mode 100644 backend/src/routes/chatmessages.js create mode 100644 backend/src/routes/chatroomparticipants.js create mode 100644 backend/src/routes/chatrooms.js create mode 100644 backend/src/services/chatmessages.js create mode 100644 backend/src/services/chatroomparticipants.js create mode 100644 backend/src/services/chatrooms.js create mode 100644 frontend/src/components/Chatmessages/CardChatmessages.tsx create mode 100644 frontend/src/components/Chatmessages/ListChatmessages.tsx create mode 100644 frontend/src/components/Chatmessages/TableChatmessages.tsx create mode 100644 frontend/src/components/Chatmessages/configureChatmessagesCols.tsx create mode 100644 frontend/src/components/Chatroomparticipants/CardChatroomparticipants.tsx create mode 100644 frontend/src/components/Chatroomparticipants/ListChatroomparticipants.tsx create mode 100644 frontend/src/components/Chatroomparticipants/TableChatroomparticipants.tsx create mode 100644 frontend/src/components/Chatroomparticipants/configureChatroomparticipantsCols.tsx create mode 100644 frontend/src/components/Chatrooms/CardChatrooms.tsx create mode 100644 frontend/src/components/Chatrooms/ListChatrooms.tsx create mode 100644 frontend/src/components/Chatrooms/TableChatrooms.tsx create mode 100644 frontend/src/components/Chatrooms/configureChatroomsCols.tsx create mode 100644 frontend/src/pages/chatmessages/[chatmessagesId].tsx create mode 100644 frontend/src/pages/chatmessages/chatmessages-edit.tsx create mode 100644 frontend/src/pages/chatmessages/chatmessages-list.tsx create mode 100644 frontend/src/pages/chatmessages/chatmessages-new.tsx create mode 100644 frontend/src/pages/chatmessages/chatmessages-table.tsx create mode 100644 frontend/src/pages/chatmessages/chatmessages-view.tsx create mode 100644 frontend/src/pages/chatroomparticipants/[chatroomparticipantsId].tsx create mode 100644 frontend/src/pages/chatroomparticipants/chatroomparticipants-edit.tsx create mode 100644 frontend/src/pages/chatroomparticipants/chatroomparticipants-list.tsx create mode 100644 frontend/src/pages/chatroomparticipants/chatroomparticipants-new.tsx create mode 100644 frontend/src/pages/chatroomparticipants/chatroomparticipants-table.tsx create mode 100644 frontend/src/pages/chatroomparticipants/chatroomparticipants-view.tsx create mode 100644 frontend/src/pages/chatrooms/[chatroomsId].tsx create mode 100644 frontend/src/pages/chatrooms/chatrooms-edit.tsx create mode 100644 frontend/src/pages/chatrooms/chatrooms-list.tsx create mode 100644 frontend/src/pages/chatrooms/chatrooms-new.tsx create mode 100644 frontend/src/pages/chatrooms/chatrooms-table.tsx create mode 100644 frontend/src/pages/chatrooms/chatrooms-view.tsx create mode 100644 frontend/src/stores/chatmessages/chatmessagesSlice.ts create mode 100644 frontend/src/stores/chatroomparticipants/chatroomparticipantsSlice.ts create mode 100644 frontend/src/stores/chatrooms/chatroomsSlice.ts diff --git a/502.html b/502.html index 53732a6..ccedbbe 100644 --- a/502.html +++ b/502.html @@ -129,7 +129,7 @@

The application is currently launching. The page will automatically refresh once site is available.

-

Adminpro

+

society-community-portal

Admin portal for management team.

diff --git a/README.md b/README.md index c2fb511..4b3e25d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Adminpro +# society-community-portal ## This project was generated by [Flatlogic Platform](https://flatlogic.com). diff --git a/app-shell/src/_schema.json b/app-shell/src/_schema.json index 4009a31..c1141d7 100644 --- a/app-shell/src/_schema.json +++ b/app-shell/src/_schema.json @@ -1,4 +1,5 @@ { "Initial version": "{\"iv\":\"DFwAjJtPtbc8CvjV\",\"encryptedData\":\"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\"}", - "Society admin portal": "{\"iv\":\"3H2yb9Cm1+0ds1Ow\",\"encryptedData\":\"FuqFxluEWNkgYhRHZu8kAP7Idl6D5qhDE6WpsIV4mbuH8YXW8jlaGVO5Zx1AOt/XCsCQ81rqdO4xLxkfgoKQ2NAiCflHaRbJcQnBBWy6Vf0VPZzKB1aZ/cBlAxpHR+UIQ1At6tNCPQ5cO7rs09Nt1xPm0sc+J9nPnWuluJg52zvGYIGsxhLKoFBPOG0cmehlZydj8gx2SzYPwxx29yqk9wXP/A+ZgapAOMLr+B4iwZ6w1O3kfFaGvc5iljz6NkvuKoBWbS30Jn1ejh/2/H+UD9N2dd3tFR7kgK1jDbXFy4TzEydjBopOGbRiT32xafGKwmMPA53vThzji7Y4vszKO7dnlfdOD8eVarL0n2x5f4p5Z1fshyF8Pu9ClUPVBOYvJxZGSe0XWvRo9s9GbdRtpJpFowKscyA8rKOTSvIwpXsesKYY2Vj410++9pZN6OCrQDnRPA5lOnCr0vvcPdZtYxLLonP1Rq7GUgVWLbPUZz3oFadPebzIULZNiA0U8mhtx1tYH+zNotOJ6kW0hPQ6x3vCBA0VCi1nHTATOqW1UMqa7K8ZVMz0D3ZoDMP5mtT9Rm6Bkp0XGCc2ouXTuuhFnZ+LUCp2PnMdMyNyZepKoNjzp8M+GQU89u1xXCY+LACNyRrnG4q1ielXtWHDAtmG5dYYnnnLtiUvKwaLjXR7iDpyZ3A607m1r1BOQxyi19i6l/cPNsAnVa2h4GNazsRqEi7eVjarAMoSUY4PFb5SpR/RwQ1A/idYCq7bfA21VgOlsuttRYH9lccU8KV/NWyV8O5B1oreHSYIoTAIgCjJmXvxUpXabmBQG8r0XgaKiI7ZzR4NvBSPzmV5LC2IuIMMzeKACR1RHjWqdNFZFzJHggx6Dr62if4QxdwJ31G0VYbXA709LjYQAN/unrU8+VuBdjAuvuh7hY8IFfcmWENEF+rl/2fmHdaqgXYCChrVBisgn09ZQGug2wvi2L8dNUGUdOLHbZ8wLhCJBNNXN2STJeiE+qXwvCkXluKRkW+29cVNioSvx1Cr5J6kG04Y0OM52Ayemertv5UrQSVAqVJrXr4i1QtlnJF4pU6nkpIOPB/evsWduJ16zEqtcBv/7AYmOnHpY6ViQjjot226mb++JWmQuexAyzJ3njRKpIkFmrTzkDNRd7PHe1delVy4JTN1i+pFfsDH0bEbTR8NmNi+buCLKy7hkYobaIy05b+ZodM5dwbLvn8F0FS1vEVUXiIrXOHJ8c6e2tZ0uN5XfoHK1CrV6hMaOMKBgM26EE9sD65OWmHsDTXLVc9TMSCxnVNnFHzx6vsGY+llgiM5JBx+9FQLKzX0LXKKhsqnNiYgzV44rYVEQrZfjPILyzyeFUcfSGd+q7Ndq9ndh7ZImHoBe4ZjYtztW6/81KozzVRPF/hLVEKbLJ4aeQSbWCB2gIDtaS19oKlHrioikKpBcnKFy418xHBagia6dSGRkCzSUiSCOlLr31ADfu5mJ8+8jKiwzeVgZhDXgiOUCyS/kepWLHAgMSxm1zLxGkJXq2k2sdwXlHvxN9ZtoMOP9yl+20mrCJmdp01G9FErn0R/uMQG9fnjJ+3vGuxAqb4WS5KkpFwaLy4WS4GlbQMeOPXA8Etz9GZWojQGlMG3L04hSQDldp5zXV8MZSJ6NpdeWhJGIshV8iJmr4c9zTU3UjFVT3mwEnJW7r1lEsfxX3M/bM5OITVyTmNMYWg3jOmKGZy67PQLW1D4LGjpcg2MFrRkS8rJ/rT79Ilcqd55Hv59I3wvbs8qSbJE+iGVi/YQx7n/B8ghJjRFA8J5z7xJ5UXbBvNr9B3U3Ss+rJyOdyAK/mnswDZig7KuNDcIRWsrDw71hWax5eCoKw8GWepFb7ZPErfYlCl3m+EK8Rv/8DIL+/haSFcscE8UGKek2k/7VexKLhkgJTlqBHgUih/0w+rU9mU0SUePtI0TvFLYGPuE5Z/QGLx0KRky8dWMW8zoJYKdz5MvTViIqUYh90TjPgP3OakZbrAicy5Hja8dP0bAFo+QCHTNezxJEXoTCO2G+Zm+09MAUG9AQqkIadH97GlD+s5dyJ5zmm6FxLI4sm9CCfDt3o9atw8NKlrIfF2B8V5HxaRh8uK2vig8+O/cUkutuuXMHuusAd2snjgJn59z4x46WWMPG95+g7etO7KwZnYPcwmX8x69KyG4AyfzKYkVZ5sV/+4hUbrNYLjN197PXXsdKqirXd+0Bd++qqAY/n1OEfYdij3izVvu+ZPBGkXR/Gmzdpfs6MWPylfpgFHqFUtIW9ZST1/tASa+xWy5o/9CM83u3bdI/kIKtImnE/BNDs1tICT+w77ZniVOZxWnUB60qQPlJwZ/yBb/fwTawBLTfoWVXABdi3yg6ux4MctaLTxFno974FwU2p2s92ZNUOzugjruWIUQ+W1q+E2daMhCRL+Z36i97B8vg1ySRSfkXFq8lKMx6ye93KbY9xCzhvpwn59NCp8QXDqbnbzbil0GC2djLkSG3J1UAHGlInmSrE0IxFbzZEwN+0U1CP2FBWmynb0TnPQ1t38xOTSjXRAZK68rB7aq+MN8YmrczxOh6ATs1zO3pL6KdZWcsIxX04wslbHQg9CeBX5sX33+cJwFOcGxSDtoQ6Ye0iBXCccW2TfxhsttdqhUoOa+XRWBmKPH8aonXO8U2x3FGczdqPqQezz1fbaUcDuKHQyVHeUx6nTebn9um5f9MHAWoz7jL9kuaD1GM6NY5Xn1VAGelIPtEFe7rfEvdmW6X0njsPm/PcvOago1atq2ZC2q/E6CQ7l+J/Mqu6gdiLfXVepB/9WokdXs4yFRI4f+feanVbZd7Hhn5a+GhoaXRPQsna3gdW74KZFJYr7NjKS15Iqo2Z2HzqPQzmNC2tjxs8aogDc3rVD7UaC/STyhbw2w9TsQSJcSfU8E6XMhYgdaWUOFoXE3woadjQMLXXVVQ4U2BqD0p1pjzX7MFj6r7MKMQ/HInijdC3WrF/X+xGMLu/uP2pXqtXRIjyQcGqh5Ly44KJMkwqJ8RJWAulcdYGNfxCkn6kI1LpPinDD10SN9CpCN1r8DiN+1+H2uONg/+D1q4XstxJl+OXshT1L8/Yzbynk48pTq/iRcCFrhm6VFVW0WbKdpk6Ad1f8oPojAF/31oglND5uWU+1S7suUe01hlX8LqtFOUA6SLHUZGN3At6Xire/pV5KjTcoRUcJhq5onGqfHZtxH03BnTxr/MwEAWN5SdnbPO0PlLY3bDdbbQQKtA3N65uZrenb7bwq7CWgvaWCx6yVo/4VYPSrEQVoNcpkJz9+JeHBPMElPT2Weoq+Q+oXAroCRs6FnVKrJnKJaBjMBpbIzxXBGm+lrrfk987XZ/HeGt0vmx0LOZjllDnAu0SPkKMH0BbN5ehaOMM7l1MbnNv5ZlWxKR+4mAHB9ssHRzlvCAwEXvXvhqTAWdU1iUcLAe9hGfW/VNGdqP0AAKjYkljOTUpw3XYqZWb8EZWjLiZhC6vijBr+gKJrZBFYUklPbKWuGn3Ib/o7CM/az4HCf3tpXewgO1PmkV+t73iqkBpdCB4RCvKnF2t3UKcuW4UJcMK1PIGw55bavOLG9rTXB4h202CDBxY09xn5pK4dcdhJsQJ4U17vxT7PaUENnQWOMzXghStTNifpkWA2CiyriKOlF7WlrGtOvFfCE6dTQAXDo9Q8k+Li32i+F+6n8UD+PgBqQq3n92GcBQaFSMieON+ILJzGrOa1iMl9Ygnw7dOYuP+lfh0YlFmCibKNaREOPnFGuZzbqERzkFJoQ+eIOXcbT72Q6u1wav3Ux9GCQA/9kBzOATCLM3GVW+LTVsb32z3NOLetid4DVm1nUW3A93ezvDDQua3MqCI+YMhC5biGQRpZ5KHGBhQ7jSgYPXi4mkp3ZTb4+QxaQo0SYjMEjZ62UvcDGEjyjYQVK666RVpqrq8eUDmCwAsSpBwy0g/8SQ6Gwcz3LY1tnqelAVX4AYyPLIIU4QuX0/LG0UB3ZRGk3nMdkOs/zig/l7HQU6YnypIG0/HbftthYTqIidBzkfgXuKiQuv30ht5YF2lRIe3FACGSHgZM2voo5SjhyMZr9k0zgMxpOz+vz+e1mGlIUKsjrIvadhYakuNHvPUVMoBIPsPNIG9FAzgLOeJTxDwYrcqYmRO3rxGu38Z/ZyzRKQC97FgKNZewTK5dxhOWY6inEUF4YR7loJE08Yc2y1VAVWwmjfid9FicdTlTosSFGDQTIP29BVA6qu2ligNAJSbQz4RLWvPUn8qRD1EDi0RGWQivLZ1w+Ty667WFPmOf1Z2CR9g/CkVTrxuBZLK1POTmwb80uL7P/I9Fkg07oSi185x4RVrRhjWzvwkigTJfUo1cUr/7fMYnkpGM6fH9i1Mzhi09UTRmTFUkkhZRDh3xg00IXdWAZJuM4KtNRFpi4PIKWFFXiDnHOkRGt+X+amVqhGJ1bqtWNMRYpFrEUgmq7l0X9ojSf+kfRoE7m08mNogIno1f0PpNplkoFSRkv+jE30gQcH0O2ov8EIx6lSR+gqXXy+uaw15uSPGRnqLHOEK/OqLWwMkfx5ncR7ncZU7U1ZyXdQqdnmxtXp/xmYUMepn+kaSZWt95JD4aETn6pubjyHe5Du9z5VFv7DWLTHFuxQWtN8p3ryQSu9LGRBZhxsa6z6vlUO0CoZoV7j+ittYeTj220ZUb7OkMDUG5jFusMTJtHrOdZ4Okm26kCEe2dFLNfsWHb93V3aDe4sGgbZSZTziRgIJbtmYemUL4sLUbqD1Q9sal1o1jWfnDlLles/P285b1OVsjOR+DKL8G5bXBPpt05LghoykvJIOp75oOtbr8cl4LMxjkx+Nb+lwvy/0kRancATpqcT/wca+yL0e5AlsHcHdXXCEv6gNW6PSab7djDuMMIxryXKtBFWj0iTyqu6u+ccembA9CGCunWJaOI5Jj92kLJhT3rcmZkKO0UoMxAdabJaFiGxAPa4xv4zg8utv1wVL90NPo85TkHq8xBjJLIEyofJ4IKiRGi7bJQzT9DmCBOZ8cL/BKI8a5trQ0W8gaFGHgUABDG+tXl/1N49nj/iRX+GyWdWwojEXqdqrRLrQQilltDzjxswCIDaRBSSUVwIjZo7EOYXjbXop0DZDmLT3poFu4EUwgtXJFIQrUD6gIfWuk+HLwDJGFo/bUtSIZuLPhn7VvU93Zpk0HipuCrPos+VnyBfWe3EIKTsdl/CW5aTiSFymyjEkS01Wy3gLE8284NRco9B356/y61kjZf2jvSoGleXpQe+QweqPC2ip+qSrAgv74bp9CDvLjEOvu8mf/glLMntOETYrvrc26TGjyA/ZVym9JaQNPvEsR+HlzxCzOCQO3JnudoEM5kNwY1czT9J4yiIW28oZsi4nSVxSh+syB39xcTycKEs4PFWZofFfcBylLEeygU2JBmFBoUsNGtbWfbQmrjNp/e5Rf92+taKKejrjy+LsbbVfDiIFgcroO6ioG+4FQDQnxq2kxY14ddzj10/FYpnsc80wKTPaovSw7q0ewkKNRWYNsq9YH/m52kzwGUZC2uaRn3khlrv8/GBFfgI+XgzF7EJ2OU8KqG3q2M4DwkNNRGNXaS9UDWsBL9sOC/PHP4dE7QQg3Ro91lFNGJ+5HC2EWe3ZGD75G8K2bJDrGdc3KUHWsqao7kascOafzELXRrELhEO3TkzMGgumSnXAUXF37UUoJPg68W4nIEVL/88PXVi4Bvb+32t+Pvl7jjAjdbuHAjn7gkjepiliF+sqDFLGjI746EHqXiWrW0DW30+v675Mmx7hWmVZBgxa+KhDPP80n2YX9rQH7KISyibkBBJ7p0G8Nx8o4AKLNrB2o1tf9yU64VZBXh6nOJMtCokQUFBhHlaf/loCLsYeeSmfxc2vGa4McgM3zocxiIjO5ZjqCMRHjRSl0cXKEXYnkLmJHFdfnbBlm/vjE3l1koCQ7LIJS03oPtBAO4oLcvf7cNaPIlaVOl0qEwoPeq4FUAMgEzmUYSRHPHApwwXZhYRUaoqJQu5g10kRspq1hpAtUA/k16N/xpsAixiZbsQVLxqe4Y1WbRoLt9hri8LvDVYC2WvUeG/Zsr3nH8HG8UAM1ZQVVyuoUgKyB35LdCgo6Bo3r3h26uJugFLVbS1mjpLpikb9bWV3cs1dpTHn3BUmdqt/4Ig2i/Qpl175jVv2LRNZwfVAxb+PyHFhciElBpxtEGnQ37kL0mnX9oNQkKMFmtGMeFjZ0qozivyNm5Rtg/phnW768We0nJd88lvcr6f0t2nQYmKrHrVOwseNPTcFEc2s86fwSXNcD2m/3FrwtklXrl/p/QhwTMePxkqABAohEMWndGePW48dkYXQQ5yssaYBManEfd4m8iidTXl69Ug/wMcKHemNYpFIKqMgoTGHJsJX3fqPzyRGOOZK8y6PFSuNLBHmgGOJqV4mpJbngusGeOL4lXHaFdpCZdqRY/2E1uWI7QF3c/FZ8v1pJwe0Kys4pcoDQN98QcyOowD2LOOdBag+9cRRJyoodf74pao8tOuGZXYsyqrnUib130EMbyiyHaZUFCqWHQifuhh/cwWkr3kXZsCuwaaz0tggMV3GkERPoXy94X6bW1rGG3GNgCVDWewDDbL6zVcMRLtqeksgdku9utF6eRi1K6xCp3hPFo06I652R2hLIeB5kb9gSMrmxzkRoqQywCMwrN1S4RJA2RKp3BkD20IAO2GdeYcY2ek/SqXacjOORJ28QwvHmmjpDx8dx597V25zBQPJNLkPU1oGyRFL8ubDEpfB/DckdKQ8Icg3wu6OaCUf5GTJLruVjTVn3MWii/yaSTrf+JGxftxsHoju1DxkrHHW7YLJNg4lOkDPEwi9E2XDS7+rgYUJF4DRiW3z/zJFaFNjwB3wN/GLRZJKxKp3q/RjpZfMq0hgKOmRrVtrq9W//P5XG7o7riuScqtDls9skjg/Fy6lr7M06ZCfCkxsa2rafVn3PsFnSAPZdkSBm+7RVNPu/b6mFPvVqy7VaQUrLHiWHnbxEHgmi86WiGV0argQOfbxftbJlKNzVBqSc8EuWcmnl6MMw84XyJECUvqz6bPy+0mtt65O/7IoLqYxpPu0N0aETVMoOK4lUrnXTMs7O/S1pxbbHT7ZtZZL6UB7qRt7aL5bfgj7kDxnUK9H+ushSnmNg0DZiYjJgiHTIDQWGQg9hza7o+xjro1n2o2ISCGK2oKMh1KR+bVn2vA41VuMMjZtWex9i7QJ72vTJVxtcFUaUTpVxWBFZpDGTJhT40zyeuL/tB2k9D3MUtDyH0wYxw/vstimjq6NsdASQN3lTOmuOv4Q6G5OnFfG7mXNeL9pOh0Rnj6lOR1dJm5f/Weuc6i0bWJvcOfW5Escf6nZZrzFcz6QfQczCV2yNqj0pEOV/tq/T4RmojqMWnBMPJ4278UiK/P2bezVDE8l2DEGskh9FjHXBPL2HwgNKhsCs17v1RbpPNMOpain+i7C8Y+lf7h5WhSHcJtuKL+ySg8H4/i9I+JF0AREQKThoxLaZvrgZ1BtHMQx/8HeGyuHr//O5Ohm1Ynmxa/+TfQLp8wCHSx9cgtCY1wf1sgiPxeZ6F1S+6cIrsdIKIwRtGgId4ysODI94hBftfe/YEYlov5Ile9ApRA/d2D0+EMef4JmkQcR5uvC2OsqMSfMjLG2WCDFB3AU9CTTRvk6xSQFOQsdWV1Q9jNwVPhFMfqcVIax6990Q3qnRx5ZAkBbKLezI3Y+mLVwBC7ZlbZFZXnRQgKUEVPDfMwelpFSax3UFetFOA2vILfL8aJJo2Bf2UUgquOtkY2AZr51wcBhHNZTG3/l3AFRdO9RgsqKs3xY93cSU8zBR2H2ubhA/EMTHaUcBJ9bA0DTk5siZpRsijua+9kRzU0ch/JyZGPbOaxDrprt98+rhZeNR+YjY5kmhVF4LwXsQQ+vH7TtkkHr1/Tk66r+pa9fAEfmk/NVaFFNc9y67tLDJgtMYYKW4R8z3kEcnXx8+zUb7DPgvF5D09zzdCC9HGxbXIkzv4HSQWo+/+NSqx6XMJh8bMEKitY0YnH50i2UoBHAF30ZdDQCrQ9Zz5r8ETJL/yk30RvOQvjpeyyf3ctOe/7DobNrXNfbDZGH9fREDClZBYmHF/XATLBO5aoQ6syxVOKZyBbIf8YCEIKLpB9rZQLE1GZ2ySu+fHfiSC0BHbd4KHW9OEDl/rBvmHuaTv0dCLsQ+OEfkN/6GtSah2vTAkgBfk7Y1rFbdahJ5PctHlNj6n4o+cHMMkwC9NG5tC0/8J/5SbA6jFGJUVbgJO3NngFj1RVHVLFY1Yadrn1iyNfPfSuLaRN2BHtxvflnNDGJBeayebrUMAKkfuuIyMcwVPzz9+BBLkA8J1p1qZuc+yidx7/ToIL9qunoLzhN0JX7VKapgxpcsr0+HMMfUDeiC1ZJRmr5tNMooXX1NZ0pk1b1RydR/Lpf6oIML0ZaUVLsJ+AXt3BFYgt133VkdFTW73YDhwnA8TZVucttZBX4SPWmTgsDYqh2KNGWGSCBZkufuelxsNDF5ycLSUVYnNFSvNlrUynbVJB6nLG7K2KoMTNylSkGs8UineTF5DFCX3yd+awj90kdV5XKwowWuO0hNwi7qNQ8t9qEP+Jx1F73ksaHdiPsYm6rklh63YV6h82oQgUAFQogD4GDwRDCeBxKZlJRmyWMlxdm3EXXSLkOVRxu29sD7fhs8KQU90B6n7SAefV0SC0J0RAP1/+AHxaeecaABPpUbQ8TYtRH0Xw0ToukI3b7pRhaS1UamoDOQ3QWSj5UClKSBb2r7WfpjbvDJ+PopZDdurp3+HLcTiQFrGV67Du1jBCVwOQtKWtDQpVoxKmdQl6aY5ZiBGYqKyi1vN2acXOAbHvzQ+RYhceBXarj+R4OLuPPg8Pm5dw1xG7DU198MHoon4kxD0Y7gcpc7GBoDvsydaKUha2/gHgrw0x35mJHmLjExSdvWVAFy1Pk/Epot9mA6fM1jzjcWOyNzshocQFyQEbkX2LysxYVFos+35V+CTQ/fnH62dCmNcgRItaDiFnZN/R2YDVVsZkxW2YtIadBTMOzoi/REjXt1F+o1ERidV3KHdte6GyB+M50j6GJ0XtObQOJnXmPpSAJ5sf0Cfn0xJeDHipGEK/L97lVDKJZbd92iF7N1ZaCChgmeDB6RKKhNgmlOjENpyHD0UH1T0MrIYSGJ59vFQlh0paeaVx9yUXf9cAsVk56v1hUBvcBi7EDqHf+0heDjalrNakT03NtAxVorhgAC+Im6rsh60F5gRXi/VPHCRzEymzx00Cq4WcMNDHpkAdjvcYUAYrjJN2No/6INET3MSOq40U/U6/IWIhQoOFTVgeTMYI9J2Q32PZRvMryFaB1IsQq79XS8XaGPUGI058caXaeyTAMtTtw96OqbRA7krydf3zIjd9+hNaW1mLPxglBHvxFvdM92R6SbtkhS01zrmhW+qnB+HT5N+1dz/S7dAbmAV97eHrzd/0dUyw09tZE+1Xu3TTHxV35UaMjM6oaizOdvbkr3vDek3g+PeihPQAeWYZTyHh7/p6GGuqPocgUh+9Nymbxo2A5YV0KZev0xniAnZUPsmBzmmDIxKVaU/LYn9LICBxFkLbKayzBJkYjbv7iTOwSv5GobPml2ZwUkif0DUo7FIrl5LF9lvIGakzc721e8HPD8wV3+wsvF8H2cN8EQnt95XH2V+Gm3nWgk+p+Jx2DvnjL+YaEnWVGC/US6slbsC8qu5A2onqoZ32Q1ETEC5Vwy/vjDDLYVmX3x/2K239x45eH0vjriMPM2Waq6wlvDp3P20hOB6OdK74W2dOLdGMaZfFKtxrL0fgRH2M+8B1TI628VNLBOnieVm8pQxcq8pflzusu9MAZrzGcqLiJUhKhfiUmXas7xKExUP40VfbF+pmrtTxkbZeJt7vKOF2WtSNru1pLOanpSZGPA1LMLarHS1cT1oVLPB8JtM4mMheoIq2AVkItOanw7rEMZIeqgY+lJSpaEVvN8mxyhWnn407C9olp4thWV+3zoDo7UDLHLu6JJDWJO2uKjZXcRoFpsIAMmJRiC7z3sMpb5ms3RqlkjIawYj/otDUSgKZjFq1lzq4PxhvI6U+DPOIwv+LPN7SJY1Aew+agEo/KdrGejHi8xkUh7IoCO1Fg97+v6b9FJ17QmsP00glXOQrkZose+Q+9w1Fq9CfOx51f77uZFo9ak6lJlv+33zcFgX2TTVl+6gRiu3UkQpJOTMFQTKeBw+c9LNuLOOzqtEu3ggEuD0lG9jksCbo3h4Lje4DiqY2ygmEjOJPiY/UHoyK7PqsA10R2BcS7aur4aQS+ePUpBe6oUmdOg2ofCTAyO74GyA1bnxINZYwCSIPsHUQMqsco8npJr2HurxeMITDbsulI6K9F1bgIHilXp1QIvTke/wml2nA9EVThPVhYXFtwpb3f+8IcnLr8H9uKngAgEdkb/icL3Ewwn2H4z6ZveJ7r8mLEDw5F5IyU407FTpGQL5vxwXR8rKee4LuXPoOdldQ6Li0VCygCSs252xHgQq4vMSG3O8haqqNP2yTwUdvoGATYmPk5iYpnZ086TzxZPirTH63IuhC+okrqAeCFBVXnbj48yTZ6W3AH/urdaxCFiqaynEgtoHNM/ryx4Hdi4XtHsLn/Q1+bJXatu8j18UD4URLXomuxM2MJcD46vwGMgtVswjpwTT6LbT73H3wJvKt+eKvqHRBoIWIeptOhzdDKeVL9lIymZIDY7tFMFwtc9uirWG7pFaECW/66Q0CT33tCPcpJfrwvkWeVJRbnK/nQIl7JlDWkrDvZr2lYQ2k9gjx5sv0+E4QveaCA9lrbTvA3hM4kukvEJQZXeZ5qF7Mj4K3V4rWqcAGrmsuZVgDA3Fz/7EJ70ql10NV7sbMDkVimSKE4Ncvx6WtEJ4fkUUP6ZK6fiVEfQxJ4+6mCjTfPC1eY9sugHymzKTMBxLLQ3zf9hHiTN3M3RZM+cdYTS3y+ZtaFoq6QdaVLcmSxZD7hwz8yB8Jpmlt1Zk71NKR8s0Pg9Tq3BIulWJA3h2W0NU0wwcpmu6kO9QqqFc14aholeN1MAEnqdh8PrqHRSZij8hZL9p0nHx6j/RMxvIOuz/1f0c+hBqzjEebH/s+0sVYI9ULo2EKA4j8Vn6o1fjsWc9+SPKzk7RGceTIAK1hwP4LWaGR8F9D+uR195mnMIjmWAXK/XBLBn4x99KACXgBgA9RnflwtymsTGgE8eFwkveTwIXpZy2Fdg40fAmAWr/ls1MNVyl6GbcjeueITyC7rO5O7NKIXN0GAtz0TQtjhwShRveIl5jSlWQxK9K/jH7cHywGMJQH0zW/StIN+W30sIae9iSF58nTB8OlEADjKZHlwm3lDc5xM/1IGlayviO8FkqgGTN0/yRvKDFpwVzdTsVLDlBvzK60slFy0p+vVYGZNYOQNcf+tTDSsDS7iixtZS/vfQsYalQ0S28tBRVKPbHvn0sHwIKHXoCyYXdaF7a+UsSkkOWPOtRMQjPF+FWnLfG/gwXbziFgkjSBltCy60Ll3HxRxvokbguLtwXqklslfenKdrNA2Xa2EOPgrJBp+ltVtRqAs+WxrYnGdmTBdV0OzdcPfjqZmPAh1466YDlIJVOhfVZwJlw1L+oGlW1l+D5Z0s7nUnsBO8YSo9O5pkSxF9sdf1p5vGGY/oM3awfb/1NHbR0FFMhvDfjqbJqmVUIchUJKkgGTccELcjhHbq9KUfucwRk8zMj0Ju1f2yyv909PTvEJysmjHKFH6EDQMRFfjMuOBAKvIkaHCdpK0oLWvfjvcgkKfddl7evz3PPYB6HbaLhWQewYUa6MvQ9BA4N6XmZDlGUg8eluQzzzpaJrH0b1EauAWQGzbh5AJPffCsnSyiu5GOn+09wMCkgkKDZtiB+PpO7y3EfJaznnrM1RuNQ1lNS4NQzng6lB89z5d+wI/JtPO3CmgZ6eNysIv3xnGAdUszm++feW0GPWT2OJAIQmDOfmr4Ys24snSuFLNKEP4nRKnwPM85BjyZyu8EzAaDwA7Ey+wLpKEesHsR82RkcuB7ZzR/fWGzH+jbuFCW7wAPYtUCIc2AXUewQ/ZEvz+9GJO9s2uMxXmtND9YcM7YuP4zItDFKbsA1\"}" + "Society admin portal": "{\"iv\":\"3H2yb9Cm1+0ds1Ow\",\"encryptedData\":\"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\"}", + "Society Community portal": "{\"iv\":\"+rK8HORjiw5Wcjed\",\"encryptedData\":\"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\"}" } \ No newline at end of file diff --git a/backend/README.md b/backend/README.md index a4ebd6c..835df29 100644 --- a/backend/README.md +++ b/backend/README.md @@ -1,4 +1,4 @@ -#Adminpro - template backend, +#society-community-portal - template backend, #### Run App on local machine: @@ -38,10 +38,10 @@ - Type this command to creating a new database. - - `postgres=> CREATE DATABASE db_adminpro;` + - `postgres=> CREATE DATABASE db_society_community_portal;` - Then give that new user privileges to the new database then quit the `psql`. - - `postgres=> GRANT ALL PRIVILEGES ON DATABASE db_adminpro TO admin;` + - `postgres=> GRANT ALL PRIVILEGES ON DATABASE db_society_community_portal TO admin;` - `postgres=> \q` --- diff --git a/backend/package.json b/backend/package.json index 285f97c..4eef1f9 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { - "name": "adminpro", - "description": "Adminpro - template backend", + "name": "societycommunityportal", + "description": "society-community-portal - template backend", "scripts": { "start": "npm run db:migrate && npm run db:seed && npm run watch", "db:migrate": "sequelize-cli db:migrate", diff --git a/backend/src/config.js b/backend/src/config.js index e08b7dd..3266419 100644 --- a/backend/src/config.js +++ b/backend/src/config.js @@ -3,7 +3,7 @@ const os = require('os'); const config = { gcloud: { bucket: 'fldemo-files', - hash: '9c474fb5916d5795b943fa0f92fc45ed', + hash: 'afeefb9d49f5b7977577876b99532ac7', }, bcrypt: { saltRounds: 12, @@ -36,7 +36,7 @@ const config = { }, uploadDir: os.tmpdir(), email: { - from: 'Adminpro ', + from: 'society-community-portal ', host: 'email-smtp.us-east-1.amazonaws.com', port: 587, auth: { diff --git a/backend/src/db/api/chatmessages.js b/backend/src/db/api/chatmessages.js new file mode 100644 index 0000000..9c9533a --- /dev/null +++ b/backend/src/db/api/chatmessages.js @@ -0,0 +1,370 @@ +const db = require('../models'); +const FileDBApi = require('./file'); +const crypto = require('crypto'); +const Utils = require('../utils'); + +const Sequelize = db.Sequelize; +const Op = Sequelize.Op; + +module.exports = class ChatmessagesDBApi { + static async create(data, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + const chatmessages = await db.chatmessages.create( + { + id: data.id || undefined, + + content: data.content || null, + created_date: data.created_date || null, + importHash: data.importHash || null, + createdById: currentUser.id, + updatedById: currentUser.id, + }, + { transaction }, + ); + + await chatmessages.setChatroom(data.chatroom || null, { + transaction, + }); + + await chatmessages.setSender(data.sender || null, { + transaction, + }); + + return chatmessages; + } + + static async bulkImport(data, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + // Prepare data - wrapping individual data transformations in a map() method + const chatmessagesData = data.map((item, index) => ({ + id: item.id || undefined, + + content: item.content || null, + created_date: item.created_date || null, + importHash: item.importHash || null, + createdById: currentUser.id, + updatedById: currentUser.id, + createdAt: new Date(Date.now() + index * 1000), + })); + + // Bulk create items + const chatmessages = await db.chatmessages.bulkCreate(chatmessagesData, { + transaction, + }); + + // For each item created, replace relation files + + return chatmessages; + } + + static async update(id, data, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + const chatmessages = await db.chatmessages.findByPk( + id, + {}, + { transaction }, + ); + + const updatePayload = {}; + + if (data.content !== undefined) updatePayload.content = data.content; + + if (data.created_date !== undefined) + updatePayload.created_date = data.created_date; + + updatePayload.updatedById = currentUser.id; + + await chatmessages.update(updatePayload, { transaction }); + + if (data.chatroom !== undefined) { + await chatmessages.setChatroom( + data.chatroom, + + { transaction }, + ); + } + + if (data.sender !== undefined) { + await chatmessages.setSender( + data.sender, + + { transaction }, + ); + } + + return chatmessages; + } + + static async deleteByIds(ids, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + const chatmessages = await db.chatmessages.findAll({ + where: { + id: { + [Op.in]: ids, + }, + }, + transaction, + }); + + await db.sequelize.transaction(async (transaction) => { + for (const record of chatmessages) { + await record.update({ deletedBy: currentUser.id }, { transaction }); + } + for (const record of chatmessages) { + await record.destroy({ transaction }); + } + }); + + return chatmessages; + } + + static async remove(id, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + const chatmessages = await db.chatmessages.findByPk(id, options); + + await chatmessages.update( + { + deletedBy: currentUser.id, + }, + { + transaction, + }, + ); + + await chatmessages.destroy({ + transaction, + }); + + return chatmessages; + } + + static async findBy(where, options) { + const transaction = (options && options.transaction) || undefined; + + const chatmessages = await db.chatmessages.findOne( + { where }, + { transaction }, + ); + + if (!chatmessages) { + return chatmessages; + } + + const output = chatmessages.get({ plain: true }); + + output.chatroom = await chatmessages.getChatroom({ + transaction, + }); + + output.sender = await chatmessages.getSender({ + transaction, + }); + + return output; + } + + static async findAll(filter, options) { + const limit = filter.limit || 0; + let offset = 0; + let where = {}; + const currentPage = +filter.page; + + offset = currentPage * limit; + + const orderBy = null; + + const transaction = (options && options.transaction) || undefined; + + let include = [ + { + model: db.chatrooms, + as: 'chatroom', + + where: filter.chatroom + ? { + [Op.or]: [ + { + id: { + [Op.in]: filter.chatroom + .split('|') + .map((term) => Utils.uuid(term)), + }, + }, + { + name: { + [Op.or]: filter.chatroom + .split('|') + .map((term) => ({ [Op.iLike]: `%${term}%` })), + }, + }, + ], + } + : {}, + }, + + { + model: db.users, + as: 'sender', + + where: filter.sender + ? { + [Op.or]: [ + { + id: { + [Op.in]: filter.sender + .split('|') + .map((term) => Utils.uuid(term)), + }, + }, + { + firstName: { + [Op.or]: filter.sender + .split('|') + .map((term) => ({ [Op.iLike]: `%${term}%` })), + }, + }, + ], + } + : {}, + }, + ]; + + if (filter) { + if (filter.id) { + where = { + ...where, + ['id']: Utils.uuid(filter.id), + }; + } + + if (filter.content) { + where = { + ...where, + [Op.and]: Utils.ilike('chatmessages', 'content', filter.content), + }; + } + + if (filter.created_dateRange) { + const [start, end] = filter.created_dateRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + created_date: { + ...where.created_date, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + created_date: { + ...where.created_date, + [Op.lte]: end, + }, + }; + } + } + + if (filter.active !== undefined) { + where = { + ...where, + active: filter.active === true || filter.active === 'true', + }; + } + + if (filter.createdAtRange) { + const [start, end] = filter.createdAtRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + ['createdAt']: { + ...where.createdAt, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + ['createdAt']: { + ...where.createdAt, + [Op.lte]: end, + }, + }; + } + } + } + + const queryOptions = { + where, + include, + distinct: true, + order: + filter.field && filter.sort + ? [[filter.field, filter.sort]] + : [['createdAt', 'desc']], + transaction: options?.transaction, + logging: console.log, + }; + + if (!options?.countOnly) { + queryOptions.limit = limit ? Number(limit) : undefined; + queryOptions.offset = offset ? Number(offset) : undefined; + } + + try { + const { rows, count } = await db.chatmessages.findAndCountAll( + queryOptions, + ); + + return { + rows: options?.countOnly ? [] : rows, + count: count, + }; + } catch (error) { + console.error('Error executing query:', error); + throw error; + } + } + + static async findAllAutocomplete(query, limit, offset) { + let where = {}; + + if (query) { + where = { + [Op.or]: [ + { ['id']: Utils.uuid(query) }, + Utils.ilike('chatmessages', 'id', query), + ], + }; + } + + const records = await db.chatmessages.findAll({ + attributes: ['id', 'id'], + where, + limit: limit ? Number(limit) : undefined, + offset: offset ? Number(offset) : undefined, + orderBy: [['id', 'ASC']], + }); + + return records.map((record) => ({ + id: record.id, + label: record.id, + })); + } +}; diff --git a/backend/src/db/api/chatroomparticipants.js b/backend/src/db/api/chatroomparticipants.js new file mode 100644 index 0000000..2bf5208 --- /dev/null +++ b/backend/src/db/api/chatroomparticipants.js @@ -0,0 +1,334 @@ +const db = require('../models'); +const FileDBApi = require('./file'); +const crypto = require('crypto'); +const Utils = require('../utils'); + +const Sequelize = db.Sequelize; +const Op = Sequelize.Op; + +module.exports = class ChatroomparticipantsDBApi { + static async create(data, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + const chatroomparticipants = await db.chatroomparticipants.create( + { + id: data.id || undefined, + + importHash: data.importHash || null, + createdById: currentUser.id, + updatedById: currentUser.id, + }, + { transaction }, + ); + + await chatroomparticipants.setChatroom(data.chatroom || null, { + transaction, + }); + + await chatroomparticipants.setUser(data.user || null, { + transaction, + }); + + return chatroomparticipants; + } + + static async bulkImport(data, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + // Prepare data - wrapping individual data transformations in a map() method + const chatroomparticipantsData = data.map((item, index) => ({ + id: item.id || undefined, + + importHash: item.importHash || null, + createdById: currentUser.id, + updatedById: currentUser.id, + createdAt: new Date(Date.now() + index * 1000), + })); + + // Bulk create items + const chatroomparticipants = await db.chatroomparticipants.bulkCreate( + chatroomparticipantsData, + { transaction }, + ); + + // For each item created, replace relation files + + return chatroomparticipants; + } + + static async update(id, data, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + const chatroomparticipants = await db.chatroomparticipants.findByPk( + id, + {}, + { transaction }, + ); + + const updatePayload = {}; + + updatePayload.updatedById = currentUser.id; + + await chatroomparticipants.update(updatePayload, { transaction }); + + if (data.chatroom !== undefined) { + await chatroomparticipants.setChatroom( + data.chatroom, + + { transaction }, + ); + } + + if (data.user !== undefined) { + await chatroomparticipants.setUser( + data.user, + + { transaction }, + ); + } + + return chatroomparticipants; + } + + static async deleteByIds(ids, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + const chatroomparticipants = await db.chatroomparticipants.findAll({ + where: { + id: { + [Op.in]: ids, + }, + }, + transaction, + }); + + await db.sequelize.transaction(async (transaction) => { + for (const record of chatroomparticipants) { + await record.update({ deletedBy: currentUser.id }, { transaction }); + } + for (const record of chatroomparticipants) { + await record.destroy({ transaction }); + } + }); + + return chatroomparticipants; + } + + static async remove(id, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + const chatroomparticipants = await db.chatroomparticipants.findByPk( + id, + options, + ); + + await chatroomparticipants.update( + { + deletedBy: currentUser.id, + }, + { + transaction, + }, + ); + + await chatroomparticipants.destroy({ + transaction, + }); + + return chatroomparticipants; + } + + static async findBy(where, options) { + const transaction = (options && options.transaction) || undefined; + + const chatroomparticipants = await db.chatroomparticipants.findOne( + { where }, + { transaction }, + ); + + if (!chatroomparticipants) { + return chatroomparticipants; + } + + const output = chatroomparticipants.get({ plain: true }); + + output.chatroom = await chatroomparticipants.getChatroom({ + transaction, + }); + + output.user = await chatroomparticipants.getUser({ + transaction, + }); + + return output; + } + + static async findAll(filter, options) { + const limit = filter.limit || 0; + let offset = 0; + let where = {}; + const currentPage = +filter.page; + + offset = currentPage * limit; + + const orderBy = null; + + const transaction = (options && options.transaction) || undefined; + + let include = [ + { + model: db.chatrooms, + as: 'chatroom', + + where: filter.chatroom + ? { + [Op.or]: [ + { + id: { + [Op.in]: filter.chatroom + .split('|') + .map((term) => Utils.uuid(term)), + }, + }, + { + name: { + [Op.or]: filter.chatroom + .split('|') + .map((term) => ({ [Op.iLike]: `%${term}%` })), + }, + }, + ], + } + : {}, + }, + + { + model: db.users, + as: 'user', + + where: filter.user + ? { + [Op.or]: [ + { + id: { + [Op.in]: filter.user + .split('|') + .map((term) => Utils.uuid(term)), + }, + }, + { + firstName: { + [Op.or]: filter.user + .split('|') + .map((term) => ({ [Op.iLike]: `%${term}%` })), + }, + }, + ], + } + : {}, + }, + ]; + + if (filter) { + if (filter.id) { + where = { + ...where, + ['id']: Utils.uuid(filter.id), + }; + } + + if (filter.active !== undefined) { + where = { + ...where, + active: filter.active === true || filter.active === 'true', + }; + } + + if (filter.createdAtRange) { + const [start, end] = filter.createdAtRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + ['createdAt']: { + ...where.createdAt, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + ['createdAt']: { + ...where.createdAt, + [Op.lte]: end, + }, + }; + } + } + } + + const queryOptions = { + where, + include, + distinct: true, + order: + filter.field && filter.sort + ? [[filter.field, filter.sort]] + : [['createdAt', 'desc']], + transaction: options?.transaction, + logging: console.log, + }; + + if (!options?.countOnly) { + queryOptions.limit = limit ? Number(limit) : undefined; + queryOptions.offset = offset ? Number(offset) : undefined; + } + + try { + const { rows, count } = await db.chatroomparticipants.findAndCountAll( + queryOptions, + ); + + return { + rows: options?.countOnly ? [] : rows, + count: count, + }; + } catch (error) { + console.error('Error executing query:', error); + throw error; + } + } + + static async findAllAutocomplete(query, limit, offset) { + let where = {}; + + if (query) { + where = { + [Op.or]: [ + { ['id']: Utils.uuid(query) }, + Utils.ilike('chatroomparticipants', 'id', query), + ], + }; + } + + const records = await db.chatroomparticipants.findAll({ + attributes: ['id', 'id'], + where, + limit: limit ? Number(limit) : undefined, + offset: offset ? Number(offset) : undefined, + orderBy: [['id', 'ASC']], + }); + + return records.map((record) => ({ + id: record.id, + label: record.id, + })); + } +}; diff --git a/backend/src/db/api/chatrooms.js b/backend/src/db/api/chatrooms.js new file mode 100644 index 0000000..3ab189f --- /dev/null +++ b/backend/src/db/api/chatrooms.js @@ -0,0 +1,297 @@ +const db = require('../models'); +const FileDBApi = require('./file'); +const crypto = require('crypto'); +const Utils = require('../utils'); + +const Sequelize = db.Sequelize; +const Op = Sequelize.Op; + +module.exports = class ChatroomsDBApi { + static async create(data, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + const chatrooms = await db.chatrooms.create( + { + id: data.id || undefined, + + name: data.name || null, + type: data.type || null, + created_date: data.created_date || null, + importHash: data.importHash || null, + createdById: currentUser.id, + updatedById: currentUser.id, + }, + { transaction }, + ); + + return chatrooms; + } + + static async bulkImport(data, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + // Prepare data - wrapping individual data transformations in a map() method + const chatroomsData = data.map((item, index) => ({ + id: item.id || undefined, + + name: item.name || null, + type: item.type || null, + created_date: item.created_date || null, + importHash: item.importHash || null, + createdById: currentUser.id, + updatedById: currentUser.id, + createdAt: new Date(Date.now() + index * 1000), + })); + + // Bulk create items + const chatrooms = await db.chatrooms.bulkCreate(chatroomsData, { + transaction, + }); + + // For each item created, replace relation files + + return chatrooms; + } + + static async update(id, data, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + const chatrooms = await db.chatrooms.findByPk(id, {}, { transaction }); + + const updatePayload = {}; + + if (data.name !== undefined) updatePayload.name = data.name; + + if (data.type !== undefined) updatePayload.type = data.type; + + if (data.created_date !== undefined) + updatePayload.created_date = data.created_date; + + updatePayload.updatedById = currentUser.id; + + await chatrooms.update(updatePayload, { transaction }); + + return chatrooms; + } + + static async deleteByIds(ids, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + const chatrooms = await db.chatrooms.findAll({ + where: { + id: { + [Op.in]: ids, + }, + }, + transaction, + }); + + await db.sequelize.transaction(async (transaction) => { + for (const record of chatrooms) { + await record.update({ deletedBy: currentUser.id }, { transaction }); + } + for (const record of chatrooms) { + await record.destroy({ transaction }); + } + }); + + return chatrooms; + } + + static async remove(id, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + const chatrooms = await db.chatrooms.findByPk(id, options); + + await chatrooms.update( + { + deletedBy: currentUser.id, + }, + { + transaction, + }, + ); + + await chatrooms.destroy({ + transaction, + }); + + return chatrooms; + } + + static async findBy(where, options) { + const transaction = (options && options.transaction) || undefined; + + const chatrooms = await db.chatrooms.findOne({ where }, { transaction }); + + if (!chatrooms) { + return chatrooms; + } + + const output = chatrooms.get({ plain: true }); + + output.chatmessages_chatroom = await chatrooms.getChatmessages_chatroom({ + transaction, + }); + + output.chatroomparticipants_chatroom = + await chatrooms.getChatroomparticipants_chatroom({ + transaction, + }); + + return output; + } + + static async findAll(filter, options) { + const limit = filter.limit || 0; + let offset = 0; + let where = {}; + const currentPage = +filter.page; + + offset = currentPage * limit; + + const orderBy = null; + + const transaction = (options && options.transaction) || undefined; + + let include = []; + + if (filter) { + if (filter.id) { + where = { + ...where, + ['id']: Utils.uuid(filter.id), + }; + } + + if (filter.name) { + where = { + ...where, + [Op.and]: Utils.ilike('chatrooms', 'name', filter.name), + }; + } + + if (filter.created_dateRange) { + const [start, end] = filter.created_dateRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + created_date: { + ...where.created_date, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + created_date: { + ...where.created_date, + [Op.lte]: end, + }, + }; + } + } + + if (filter.active !== undefined) { + where = { + ...where, + active: filter.active === true || filter.active === 'true', + }; + } + + if (filter.type) { + where = { + ...where, + type: filter.type, + }; + } + + if (filter.createdAtRange) { + const [start, end] = filter.createdAtRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + ['createdAt']: { + ...where.createdAt, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + ['createdAt']: { + ...where.createdAt, + [Op.lte]: end, + }, + }; + } + } + } + + const queryOptions = { + where, + include, + distinct: true, + order: + filter.field && filter.sort + ? [[filter.field, filter.sort]] + : [['createdAt', 'desc']], + transaction: options?.transaction, + logging: console.log, + }; + + if (!options?.countOnly) { + queryOptions.limit = limit ? Number(limit) : undefined; + queryOptions.offset = offset ? Number(offset) : undefined; + } + + try { + const { rows, count } = await db.chatrooms.findAndCountAll(queryOptions); + + return { + rows: options?.countOnly ? [] : rows, + count: count, + }; + } catch (error) { + console.error('Error executing query:', error); + throw error; + } + } + + static async findAllAutocomplete(query, limit, offset) { + let where = {}; + + if (query) { + where = { + [Op.or]: [ + { ['id']: Utils.uuid(query) }, + Utils.ilike('chatrooms', 'name', query), + ], + }; + } + + const records = await db.chatrooms.findAll({ + attributes: ['id', 'name'], + where, + limit: limit ? Number(limit) : undefined, + offset: offset ? Number(offset) : undefined, + orderBy: [['name', 'ASC']], + }); + + return records.map((record) => ({ + id: record.id, + label: record.name, + })); + } +}; diff --git a/backend/src/db/api/users.js b/backend/src/db/api/users.js index 3a11e69..daac9c1 100644 --- a/backend/src/db/api/users.js +++ b/backend/src/db/api/users.js @@ -267,6 +267,16 @@ module.exports = class UsersDBApi { const output = users.get({ plain: true }); + output.chatmessages_sender = await users.getChatmessages_sender({ + transaction, + }); + + output.chatroomparticipants_user = await users.getChatroomparticipants_user( + { + transaction, + }, + ); + output.avatar = await users.getAvatar({ transaction, }); diff --git a/backend/src/db/db.config.js b/backend/src/db/db.config.js index b6a6f18..aae76fb 100644 --- a/backend/src/db/db.config.js +++ b/backend/src/db/db.config.js @@ -13,7 +13,7 @@ module.exports = { username: 'postgres', dialect: 'postgres', password: '', - database: 'db_adminpro', + database: 'db_society_community_portal', host: process.env.DB_HOST || 'localhost', logging: console.log, seederStorage: 'sequelize', diff --git a/backend/src/db/migrations/1750265371768.js b/backend/src/db/migrations/1750265371768.js new file mode 100644 index 0000000..5f83729 --- /dev/null +++ b/backend/src/db/migrations/1750265371768.js @@ -0,0 +1,72 @@ +module.exports = { + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async up(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.createTable( + 'chatrooms', + { + id: { + type: Sequelize.DataTypes.UUID, + defaultValue: Sequelize.DataTypes.UUIDV4, + primaryKey: true, + }, + createdById: { + type: Sequelize.DataTypes.UUID, + references: { + key: 'id', + model: 'users', + }, + }, + updatedById: { + type: Sequelize.DataTypes.UUID, + references: { + key: 'id', + model: 'users', + }, + }, + createdAt: { type: Sequelize.DataTypes.DATE }, + updatedAt: { type: Sequelize.DataTypes.DATE }, + deletedAt: { type: Sequelize.DataTypes.DATE }, + importHash: { + type: Sequelize.DataTypes.STRING(255), + allowNull: true, + unique: true, + }, + }, + { transaction }, + ); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async down(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.dropTable('chatrooms', { transaction }); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, +}; diff --git a/backend/src/db/migrations/1750265419400.js b/backend/src/db/migrations/1750265419400.js new file mode 100644 index 0000000..ccdeb03 --- /dev/null +++ b/backend/src/db/migrations/1750265419400.js @@ -0,0 +1,47 @@ +module.exports = { + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async up(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.addColumn( + 'chatrooms', + 'name', + { + type: Sequelize.DataTypes.TEXT, + }, + { transaction }, + ); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async down(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.removeColumn('chatrooms', 'name', { transaction }); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, +}; diff --git a/backend/src/db/migrations/1750265455323.js b/backend/src/db/migrations/1750265455323.js new file mode 100644 index 0000000..2093416 --- /dev/null +++ b/backend/src/db/migrations/1750265455323.js @@ -0,0 +1,49 @@ +module.exports = { + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async up(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.addColumn( + 'chatrooms', + 'type', + { + type: Sequelize.DataTypes.ENUM, + + values: ['value'], + }, + { transaction }, + ); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async down(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.removeColumn('chatrooms', 'type', { transaction }); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, +}; diff --git a/backend/src/db/migrations/1750265515961.js b/backend/src/db/migrations/1750265515961.js new file mode 100644 index 0000000..eb06bce --- /dev/null +++ b/backend/src/db/migrations/1750265515961.js @@ -0,0 +1,72 @@ +module.exports = { + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async up(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.createTable( + 'chatmessages', + { + id: { + type: Sequelize.DataTypes.UUID, + defaultValue: Sequelize.DataTypes.UUIDV4, + primaryKey: true, + }, + createdById: { + type: Sequelize.DataTypes.UUID, + references: { + key: 'id', + model: 'users', + }, + }, + updatedById: { + type: Sequelize.DataTypes.UUID, + references: { + key: 'id', + model: 'users', + }, + }, + createdAt: { type: Sequelize.DataTypes.DATE }, + updatedAt: { type: Sequelize.DataTypes.DATE }, + deletedAt: { type: Sequelize.DataTypes.DATE }, + importHash: { + type: Sequelize.DataTypes.STRING(255), + allowNull: true, + unique: true, + }, + }, + { transaction }, + ); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async down(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.dropTable('chatmessages', { transaction }); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, +}; diff --git a/backend/src/db/migrations/1750265548741.js b/backend/src/db/migrations/1750265548741.js new file mode 100644 index 0000000..b78bd05 --- /dev/null +++ b/backend/src/db/migrations/1750265548741.js @@ -0,0 +1,49 @@ +module.exports = { + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async up(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.addColumn( + 'chatrooms', + 'created_date', + { + type: Sequelize.DataTypes.DATE, + }, + { transaction }, + ); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async down(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.removeColumn('chatrooms', 'created_date', { + transaction, + }); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, +}; diff --git a/backend/src/db/migrations/1750265578751.js b/backend/src/db/migrations/1750265578751.js new file mode 100644 index 0000000..144a251 --- /dev/null +++ b/backend/src/db/migrations/1750265578751.js @@ -0,0 +1,49 @@ +module.exports = { + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async up(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.addColumn( + 'chatmessages', + 'content', + { + type: Sequelize.DataTypes.TEXT, + }, + { transaction }, + ); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async down(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.removeColumn('chatmessages', 'content', { + transaction, + }); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, +}; diff --git a/backend/src/db/migrations/1750265604945.js b/backend/src/db/migrations/1750265604945.js new file mode 100644 index 0000000..037132a --- /dev/null +++ b/backend/src/db/migrations/1750265604945.js @@ -0,0 +1,49 @@ +module.exports = { + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async up(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.addColumn( + 'chatmessages', + 'created_date', + { + type: Sequelize.DataTypes.DATE, + }, + { transaction }, + ); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async down(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.removeColumn('chatmessages', 'created_date', { + transaction, + }); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, +}; diff --git a/backend/src/db/migrations/1750265634411.js b/backend/src/db/migrations/1750265634411.js new file mode 100644 index 0000000..9000cdf --- /dev/null +++ b/backend/src/db/migrations/1750265634411.js @@ -0,0 +1,72 @@ +module.exports = { + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async up(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.createTable( + 'chatroomparticipants', + { + id: { + type: Sequelize.DataTypes.UUID, + defaultValue: Sequelize.DataTypes.UUIDV4, + primaryKey: true, + }, + createdById: { + type: Sequelize.DataTypes.UUID, + references: { + key: 'id', + model: 'users', + }, + }, + updatedById: { + type: Sequelize.DataTypes.UUID, + references: { + key: 'id', + model: 'users', + }, + }, + createdAt: { type: Sequelize.DataTypes.DATE }, + updatedAt: { type: Sequelize.DataTypes.DATE }, + deletedAt: { type: Sequelize.DataTypes.DATE }, + importHash: { + type: Sequelize.DataTypes.STRING(255), + allowNull: true, + unique: true, + }, + }, + { transaction }, + ); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async down(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.dropTable('chatroomparticipants', { transaction }); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, +}; diff --git a/backend/src/db/migrations/1750265662159.js b/backend/src/db/migrations/1750265662159.js new file mode 100644 index 0000000..ee3dbe9 --- /dev/null +++ b/backend/src/db/migrations/1750265662159.js @@ -0,0 +1,54 @@ +module.exports = { + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async up(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.addColumn( + 'chatroomparticipants', + 'chatroomId', + { + type: Sequelize.DataTypes.UUID, + + references: { + model: 'chatrooms', + key: 'id', + }, + }, + { transaction }, + ); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async down(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.removeColumn('chatroomparticipants', 'chatroomId', { + transaction, + }); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, +}; diff --git a/backend/src/db/migrations/1750265689235.js b/backend/src/db/migrations/1750265689235.js new file mode 100644 index 0000000..55fb170 --- /dev/null +++ b/backend/src/db/migrations/1750265689235.js @@ -0,0 +1,54 @@ +module.exports = { + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async up(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.addColumn( + 'chatmessages', + 'chatroomId', + { + type: Sequelize.DataTypes.UUID, + + references: { + model: 'chatrooms', + key: 'id', + }, + }, + { transaction }, + ); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async down(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.removeColumn('chatmessages', 'chatroomId', { + transaction, + }); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, +}; diff --git a/backend/src/db/migrations/1750265715124.js b/backend/src/db/migrations/1750265715124.js new file mode 100644 index 0000000..82a65c4 --- /dev/null +++ b/backend/src/db/migrations/1750265715124.js @@ -0,0 +1,54 @@ +module.exports = { + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async up(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.addColumn( + 'chatroomparticipants', + 'userId', + { + type: Sequelize.DataTypes.UUID, + + references: { + model: 'users', + key: 'id', + }, + }, + { transaction }, + ); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async down(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.removeColumn('chatroomparticipants', 'userId', { + transaction, + }); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, +}; diff --git a/backend/src/db/migrations/1750265741614.js b/backend/src/db/migrations/1750265741614.js new file mode 100644 index 0000000..cf6f5ff --- /dev/null +++ b/backend/src/db/migrations/1750265741614.js @@ -0,0 +1,54 @@ +module.exports = { + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async up(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.addColumn( + 'chatmessages', + 'senderId', + { + type: Sequelize.DataTypes.UUID, + + references: { + model: 'users', + key: 'id', + }, + }, + { transaction }, + ); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async down(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + await queryInterface.removeColumn('chatmessages', 'senderId', { + transaction, + }); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, +}; diff --git a/backend/src/db/models/chatmessages.js b/backend/src/db/models/chatmessages.js new file mode 100644 index 0000000..a28d989 --- /dev/null +++ b/backend/src/db/models/chatmessages.js @@ -0,0 +1,69 @@ +const config = require('../../config'); +const providers = config.providers; +const crypto = require('crypto'); +const bcrypt = require('bcrypt'); +const moment = require('moment'); + +module.exports = function (sequelize, DataTypes) { + const chatmessages = sequelize.define( + 'chatmessages', + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true, + }, + + content: { + type: DataTypes.TEXT, + }, + + created_date: { + type: DataTypes.DATE, + }, + + importHash: { + type: DataTypes.STRING(255), + allowNull: true, + unique: true, + }, + }, + { + timestamps: true, + paranoid: true, + freezeTableName: true, + }, + ); + + chatmessages.associate = (db) => { + /// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity + + //end loop + + db.chatmessages.belongsTo(db.chatrooms, { + as: 'chatroom', + foreignKey: { + name: 'chatroomId', + }, + constraints: false, + }); + + db.chatmessages.belongsTo(db.users, { + as: 'sender', + foreignKey: { + name: 'senderId', + }, + constraints: false, + }); + + db.chatmessages.belongsTo(db.users, { + as: 'createdBy', + }); + + db.chatmessages.belongsTo(db.users, { + as: 'updatedBy', + }); + }; + + return chatmessages; +}; diff --git a/backend/src/db/models/chatroomparticipants.js b/backend/src/db/models/chatroomparticipants.js new file mode 100644 index 0000000..cbdc007 --- /dev/null +++ b/backend/src/db/models/chatroomparticipants.js @@ -0,0 +1,61 @@ +const config = require('../../config'); +const providers = config.providers; +const crypto = require('crypto'); +const bcrypt = require('bcrypt'); +const moment = require('moment'); + +module.exports = function (sequelize, DataTypes) { + const chatroomparticipants = sequelize.define( + 'chatroomparticipants', + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true, + }, + + importHash: { + type: DataTypes.STRING(255), + allowNull: true, + unique: true, + }, + }, + { + timestamps: true, + paranoid: true, + freezeTableName: true, + }, + ); + + chatroomparticipants.associate = (db) => { + /// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity + + //end loop + + db.chatroomparticipants.belongsTo(db.chatrooms, { + as: 'chatroom', + foreignKey: { + name: 'chatroomId', + }, + constraints: false, + }); + + db.chatroomparticipants.belongsTo(db.users, { + as: 'user', + foreignKey: { + name: 'userId', + }, + constraints: false, + }); + + db.chatroomparticipants.belongsTo(db.users, { + as: 'createdBy', + }); + + db.chatroomparticipants.belongsTo(db.users, { + as: 'updatedBy', + }); + }; + + return chatroomparticipants; +}; diff --git a/backend/src/db/models/chatrooms.js b/backend/src/db/models/chatrooms.js new file mode 100644 index 0000000..b52d3b9 --- /dev/null +++ b/backend/src/db/models/chatrooms.js @@ -0,0 +1,75 @@ +const config = require('../../config'); +const providers = config.providers; +const crypto = require('crypto'); +const bcrypt = require('bcrypt'); +const moment = require('moment'); + +module.exports = function (sequelize, DataTypes) { + const chatrooms = sequelize.define( + 'chatrooms', + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true, + }, + + name: { + type: DataTypes.TEXT, + }, + + type: { + type: DataTypes.ENUM, + + values: ['value'], + }, + + created_date: { + type: DataTypes.DATE, + }, + + importHash: { + type: DataTypes.STRING(255), + allowNull: true, + unique: true, + }, + }, + { + timestamps: true, + paranoid: true, + freezeTableName: true, + }, + ); + + chatrooms.associate = (db) => { + /// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity + + db.chatrooms.hasMany(db.chatmessages, { + as: 'chatmessages_chatroom', + foreignKey: { + name: 'chatroomId', + }, + constraints: false, + }); + + db.chatrooms.hasMany(db.chatroomparticipants, { + as: 'chatroomparticipants_chatroom', + foreignKey: { + name: 'chatroomId', + }, + constraints: false, + }); + + //end loop + + db.chatrooms.belongsTo(db.users, { + as: 'createdBy', + }); + + db.chatrooms.belongsTo(db.users, { + as: 'updatedBy', + }); + }; + + return chatrooms; +}; diff --git a/backend/src/db/models/users.js b/backend/src/db/models/users.js index 74e549e..60179d4 100644 --- a/backend/src/db/models/users.js +++ b/backend/src/db/models/users.js @@ -102,6 +102,22 @@ module.exports = function (sequelize, DataTypes) { /// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity + db.users.hasMany(db.chatmessages, { + as: 'chatmessages_sender', + foreignKey: { + name: 'senderId', + }, + constraints: false, + }); + + db.users.hasMany(db.chatroomparticipants, { + as: 'chatroomparticipants_user', + foreignKey: { + name: 'userId', + }, + constraints: false, + }); + //end loop db.users.belongsTo(db.roles, { diff --git a/backend/src/db/seeders/20200430130760-user-roles.js b/backend/src/db/seeders/20200430130760-user-roles.js index 6314b53..ace75c1 100644 --- a/backend/src/db/seeders/20200430130760-user-roles.js +++ b/backend/src/db/seeders/20200430130760-user-roles.js @@ -104,6 +104,9 @@ module.exports = { 'reports', 'roles', 'permissions', + 'chatrooms', + 'chatmessages', + 'chatroomparticipants', , ]; await queryInterface.bulkInsert( @@ -529,6 +532,81 @@ primary key ("roles_permissionsId", "permissionId") permissionId: getId('DELETE_PERMISSIONS'), }, + { + createdAt, + updatedAt, + roles_permissionsId: getId('Administrator'), + permissionId: getId('CREATE_CHATROOMS'), + }, + { + createdAt, + updatedAt, + roles_permissionsId: getId('Administrator'), + permissionId: getId('READ_CHATROOMS'), + }, + { + createdAt, + updatedAt, + roles_permissionsId: getId('Administrator'), + permissionId: getId('UPDATE_CHATROOMS'), + }, + { + createdAt, + updatedAt, + roles_permissionsId: getId('Administrator'), + permissionId: getId('DELETE_CHATROOMS'), + }, + + { + createdAt, + updatedAt, + roles_permissionsId: getId('Administrator'), + permissionId: getId('CREATE_CHATMESSAGES'), + }, + { + createdAt, + updatedAt, + roles_permissionsId: getId('Administrator'), + permissionId: getId('READ_CHATMESSAGES'), + }, + { + createdAt, + updatedAt, + roles_permissionsId: getId('Administrator'), + permissionId: getId('UPDATE_CHATMESSAGES'), + }, + { + createdAt, + updatedAt, + roles_permissionsId: getId('Administrator'), + permissionId: getId('DELETE_CHATMESSAGES'), + }, + + { + createdAt, + updatedAt, + roles_permissionsId: getId('Administrator'), + permissionId: getId('CREATE_CHATROOMPARTICIPANTS'), + }, + { + createdAt, + updatedAt, + roles_permissionsId: getId('Administrator'), + permissionId: getId('READ_CHATROOMPARTICIPANTS'), + }, + { + createdAt, + updatedAt, + roles_permissionsId: getId('Administrator'), + permissionId: getId('UPDATE_CHATROOMPARTICIPANTS'), + }, + { + createdAt, + updatedAt, + roles_permissionsId: getId('Administrator'), + permissionId: getId('DELETE_CHATROOMPARTICIPANTS'), + }, + { createdAt, updatedAt, diff --git a/backend/src/db/seeders/20231127130745-sample-data.js b/backend/src/db/seeders/20231127130745-sample-data.js index 53b3954..f0f7db6 100644 --- a/backend/src/db/seeders/20231127130745-sample-data.js +++ b/backend/src/db/seeders/20231127130745-sample-data.js @@ -5,6 +5,12 @@ const Departments = db.departments; const Reports = db.reports; +const Chatrooms = db.chatrooms; + +const Chatmessages = db.chatmessages; + +const Chatroomparticipants = db.chatroomparticipants; + const DepartmentsData = [ { name: 'Human Resources', @@ -29,6 +35,12 @@ const DepartmentsData = [ // type code here for "relation_many" field }, + + { + name: 'Sales', + + // type code here for "relation_many" field + }, ]; const ReportsData = [ @@ -55,21 +67,391 @@ const ReportsData = [ created_date: new Date('2023-04-05T11:15:00Z'), }, + + { + title: 'Sales Performance Review', + + created_date: new Date('2023-05-25T16:45:00Z'), + }, +]; + +const ChatroomsData = [ + { + name: 'Charles Darwin', + + type: 'value', + + created_date: new Date(Date.now()), + }, + + { + name: 'Emil Fischer', + + type: 'value', + + created_date: new Date(Date.now()), + }, + + { + name: 'Johannes Kepler', + + type: 'value', + + created_date: new Date(Date.now()), + }, + + { + name: 'Dmitri Mendeleev', + + type: 'value', + + created_date: new Date(Date.now()), + }, + + { + name: 'August Kekule', + + type: 'value', + + created_date: new Date(Date.now()), + }, +]; + +const ChatmessagesData = [ + { + content: 'Max Planck', + + created_date: new Date(Date.now()), + + // type code here for "relation_one" field + + // type code here for "relation_one" field + }, + + { + content: 'Enrico Fermi', + + created_date: new Date(Date.now()), + + // type code here for "relation_one" field + + // type code here for "relation_one" field + }, + + { + content: 'Charles Darwin', + + created_date: new Date(Date.now()), + + // type code here for "relation_one" field + + // type code here for "relation_one" field + }, + + { + content: 'Franz Boas', + + created_date: new Date(Date.now()), + + // type code here for "relation_one" field + + // type code here for "relation_one" field + }, + + { + content: 'Dmitri Mendeleev', + + created_date: new Date(Date.now()), + + // type code here for "relation_one" field + + // type code here for "relation_one" field + }, +]; + +const ChatroomparticipantsData = [ + { + // type code here for "relation_one" field + // type code here for "relation_one" field + }, + + { + // type code here for "relation_one" field + // type code here for "relation_one" field + }, + + { + // type code here for "relation_one" field + // type code here for "relation_one" field + }, + + { + // type code here for "relation_one" field + // type code here for "relation_one" field + }, + + { + // type code here for "relation_one" field + // type code here for "relation_one" field + }, ]; // Similar logic for "relation_many" // Similar logic for "relation_many" +async function associateChatmessageWithChatroom() { + const relatedChatroom0 = await Chatrooms.findOne({ + offset: Math.floor(Math.random() * (await Chatrooms.count())), + }); + const Chatmessage0 = await Chatmessages.findOne({ + order: [['id', 'ASC']], + offset: 0, + }); + if (Chatmessage0?.setChatroom) { + await Chatmessage0.setChatroom(relatedChatroom0); + } + + const relatedChatroom1 = await Chatrooms.findOne({ + offset: Math.floor(Math.random() * (await Chatrooms.count())), + }); + const Chatmessage1 = await Chatmessages.findOne({ + order: [['id', 'ASC']], + offset: 1, + }); + if (Chatmessage1?.setChatroom) { + await Chatmessage1.setChatroom(relatedChatroom1); + } + + const relatedChatroom2 = await Chatrooms.findOne({ + offset: Math.floor(Math.random() * (await Chatrooms.count())), + }); + const Chatmessage2 = await Chatmessages.findOne({ + order: [['id', 'ASC']], + offset: 2, + }); + if (Chatmessage2?.setChatroom) { + await Chatmessage2.setChatroom(relatedChatroom2); + } + + const relatedChatroom3 = await Chatrooms.findOne({ + offset: Math.floor(Math.random() * (await Chatrooms.count())), + }); + const Chatmessage3 = await Chatmessages.findOne({ + order: [['id', 'ASC']], + offset: 3, + }); + if (Chatmessage3?.setChatroom) { + await Chatmessage3.setChatroom(relatedChatroom3); + } + + const relatedChatroom4 = await Chatrooms.findOne({ + offset: Math.floor(Math.random() * (await Chatrooms.count())), + }); + const Chatmessage4 = await Chatmessages.findOne({ + order: [['id', 'ASC']], + offset: 4, + }); + if (Chatmessage4?.setChatroom) { + await Chatmessage4.setChatroom(relatedChatroom4); + } +} + +async function associateChatmessageWithSender() { + const relatedSender0 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), + }); + const Chatmessage0 = await Chatmessages.findOne({ + order: [['id', 'ASC']], + offset: 0, + }); + if (Chatmessage0?.setSender) { + await Chatmessage0.setSender(relatedSender0); + } + + const relatedSender1 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), + }); + const Chatmessage1 = await Chatmessages.findOne({ + order: [['id', 'ASC']], + offset: 1, + }); + if (Chatmessage1?.setSender) { + await Chatmessage1.setSender(relatedSender1); + } + + const relatedSender2 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), + }); + const Chatmessage2 = await Chatmessages.findOne({ + order: [['id', 'ASC']], + offset: 2, + }); + if (Chatmessage2?.setSender) { + await Chatmessage2.setSender(relatedSender2); + } + + const relatedSender3 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), + }); + const Chatmessage3 = await Chatmessages.findOne({ + order: [['id', 'ASC']], + offset: 3, + }); + if (Chatmessage3?.setSender) { + await Chatmessage3.setSender(relatedSender3); + } + + const relatedSender4 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), + }); + const Chatmessage4 = await Chatmessages.findOne({ + order: [['id', 'ASC']], + offset: 4, + }); + if (Chatmessage4?.setSender) { + await Chatmessage4.setSender(relatedSender4); + } +} + +async function associateChatroomparticipantWithChatroom() { + const relatedChatroom0 = await Chatrooms.findOne({ + offset: Math.floor(Math.random() * (await Chatrooms.count())), + }); + const Chatroomparticipant0 = await Chatroomparticipants.findOne({ + order: [['id', 'ASC']], + offset: 0, + }); + if (Chatroomparticipant0?.setChatroom) { + await Chatroomparticipant0.setChatroom(relatedChatroom0); + } + + const relatedChatroom1 = await Chatrooms.findOne({ + offset: Math.floor(Math.random() * (await Chatrooms.count())), + }); + const Chatroomparticipant1 = await Chatroomparticipants.findOne({ + order: [['id', 'ASC']], + offset: 1, + }); + if (Chatroomparticipant1?.setChatroom) { + await Chatroomparticipant1.setChatroom(relatedChatroom1); + } + + const relatedChatroom2 = await Chatrooms.findOne({ + offset: Math.floor(Math.random() * (await Chatrooms.count())), + }); + const Chatroomparticipant2 = await Chatroomparticipants.findOne({ + order: [['id', 'ASC']], + offset: 2, + }); + if (Chatroomparticipant2?.setChatroom) { + await Chatroomparticipant2.setChatroom(relatedChatroom2); + } + + const relatedChatroom3 = await Chatrooms.findOne({ + offset: Math.floor(Math.random() * (await Chatrooms.count())), + }); + const Chatroomparticipant3 = await Chatroomparticipants.findOne({ + order: [['id', 'ASC']], + offset: 3, + }); + if (Chatroomparticipant3?.setChatroom) { + await Chatroomparticipant3.setChatroom(relatedChatroom3); + } + + const relatedChatroom4 = await Chatrooms.findOne({ + offset: Math.floor(Math.random() * (await Chatrooms.count())), + }); + const Chatroomparticipant4 = await Chatroomparticipants.findOne({ + order: [['id', 'ASC']], + offset: 4, + }); + if (Chatroomparticipant4?.setChatroom) { + await Chatroomparticipant4.setChatroom(relatedChatroom4); + } +} + +async function associateChatroomparticipantWithUser() { + const relatedUser0 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), + }); + const Chatroomparticipant0 = await Chatroomparticipants.findOne({ + order: [['id', 'ASC']], + offset: 0, + }); + if (Chatroomparticipant0?.setUser) { + await Chatroomparticipant0.setUser(relatedUser0); + } + + const relatedUser1 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), + }); + const Chatroomparticipant1 = await Chatroomparticipants.findOne({ + order: [['id', 'ASC']], + offset: 1, + }); + if (Chatroomparticipant1?.setUser) { + await Chatroomparticipant1.setUser(relatedUser1); + } + + const relatedUser2 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), + }); + const Chatroomparticipant2 = await Chatroomparticipants.findOne({ + order: [['id', 'ASC']], + offset: 2, + }); + if (Chatroomparticipant2?.setUser) { + await Chatroomparticipant2.setUser(relatedUser2); + } + + const relatedUser3 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), + }); + const Chatroomparticipant3 = await Chatroomparticipants.findOne({ + order: [['id', 'ASC']], + offset: 3, + }); + if (Chatroomparticipant3?.setUser) { + await Chatroomparticipant3.setUser(relatedUser3); + } + + const relatedUser4 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), + }); + const Chatroomparticipant4 = await Chatroomparticipants.findOne({ + order: [['id', 'ASC']], + offset: 4, + }); + if (Chatroomparticipant4?.setUser) { + await Chatroomparticipant4.setUser(relatedUser4); + } +} + module.exports = { up: async (queryInterface, Sequelize) => { await Departments.bulkCreate(DepartmentsData); await Reports.bulkCreate(ReportsData); + await Chatrooms.bulkCreate(ChatroomsData); + + await Chatmessages.bulkCreate(ChatmessagesData); + + await Chatroomparticipants.bulkCreate(ChatroomparticipantsData); + await Promise.all([ // Similar logic for "relation_many" + // Similar logic for "relation_many" + + await associateChatmessageWithChatroom(), + + await associateChatmessageWithSender(), + + await associateChatroomparticipantWithChatroom(), + + await associateChatroomparticipantWithUser(), ]); }, @@ -77,5 +459,11 @@ module.exports = { await queryInterface.bulkDelete('departments', null, {}); await queryInterface.bulkDelete('reports', null, {}); + + await queryInterface.bulkDelete('chatrooms', null, {}); + + await queryInterface.bulkDelete('chatmessages', null, {}); + + await queryInterface.bulkDelete('chatroomparticipants', null, {}); }, }; diff --git a/backend/src/db/seeders/20250618164931.js b/backend/src/db/seeders/20250618164931.js new file mode 100644 index 0000000..f3d55e8 --- /dev/null +++ b/backend/src/db/seeders/20250618164931.js @@ -0,0 +1,87 @@ +const { v4: uuid } = require('uuid'); +const db = require('../models'); +const Sequelize = require('sequelize'); +const config = require('../../config'); + +module.exports = { + /** + * @param{import("sequelize").QueryInterface} queryInterface + * @return {Promise} + */ + async up(queryInterface) { + const createdAt = new Date(); + const updatedAt = new Date(); + + /** @type {Map} */ + const idMap = new Map(); + + /** + * @param {string} key + * @return {string} + */ + function getId(key) { + if (idMap.has(key)) { + return idMap.get(key); + } + const id = uuid(); + idMap.set(key, id); + return id; + } + + /** + * @param {string} name + */ + function createPermissions(name) { + return [ + { + id: getId(`CREATE_${name.toUpperCase()}`), + createdAt, + updatedAt, + name: `CREATE_${name.toUpperCase()}`, + }, + { + id: getId(`READ_${name.toUpperCase()}`), + createdAt, + updatedAt, + name: `READ_${name.toUpperCase()}`, + }, + { + id: getId(`UPDATE_${name.toUpperCase()}`), + createdAt, + updatedAt, + name: `UPDATE_${name.toUpperCase()}`, + }, + { + id: getId(`DELETE_${name.toUpperCase()}`), + createdAt, + updatedAt, + name: `DELETE_${name.toUpperCase()}`, + }, + ]; + } + + const entities = ['chatrooms']; + + const createdPermissions = entities.flatMap(createPermissions); + + // Add permissions to database + await queryInterface.bulkInsert('permissions', createdPermissions); + // Get permissions ids + const permissionsIds = createdPermissions.map((p) => p.id); + // Get admin role + const adminRole = await db.roles.findOne({ + where: { name: config.roles.admin }, + }); + + if (adminRole) { + // Add permissions to admin role if it exists + await adminRole.addPermissions(permissionsIds); + } + }, + down: async (queryInterface, Sequelize) => { + await queryInterface.bulkDelete( + 'permissions', + entities.flatMap(createPermissions), + ); + }, +}; diff --git a/backend/src/db/seeders/20250618165155.js b/backend/src/db/seeders/20250618165155.js new file mode 100644 index 0000000..44634a0 --- /dev/null +++ b/backend/src/db/seeders/20250618165155.js @@ -0,0 +1,87 @@ +const { v4: uuid } = require('uuid'); +const db = require('../models'); +const Sequelize = require('sequelize'); +const config = require('../../config'); + +module.exports = { + /** + * @param{import("sequelize").QueryInterface} queryInterface + * @return {Promise} + */ + async up(queryInterface) { + const createdAt = new Date(); + const updatedAt = new Date(); + + /** @type {Map} */ + const idMap = new Map(); + + /** + * @param {string} key + * @return {string} + */ + function getId(key) { + if (idMap.has(key)) { + return idMap.get(key); + } + const id = uuid(); + idMap.set(key, id); + return id; + } + + /** + * @param {string} name + */ + function createPermissions(name) { + return [ + { + id: getId(`CREATE_${name.toUpperCase()}`), + createdAt, + updatedAt, + name: `CREATE_${name.toUpperCase()}`, + }, + { + id: getId(`READ_${name.toUpperCase()}`), + createdAt, + updatedAt, + name: `READ_${name.toUpperCase()}`, + }, + { + id: getId(`UPDATE_${name.toUpperCase()}`), + createdAt, + updatedAt, + name: `UPDATE_${name.toUpperCase()}`, + }, + { + id: getId(`DELETE_${name.toUpperCase()}`), + createdAt, + updatedAt, + name: `DELETE_${name.toUpperCase()}`, + }, + ]; + } + + const entities = ['chatmessages']; + + const createdPermissions = entities.flatMap(createPermissions); + + // Add permissions to database + await queryInterface.bulkInsert('permissions', createdPermissions); + // Get permissions ids + const permissionsIds = createdPermissions.map((p) => p.id); + // Get admin role + const adminRole = await db.roles.findOne({ + where: { name: config.roles.admin }, + }); + + if (adminRole) { + // Add permissions to admin role if it exists + await adminRole.addPermissions(permissionsIds); + } + }, + down: async (queryInterface, Sequelize) => { + await queryInterface.bulkDelete( + 'permissions', + entities.flatMap(createPermissions), + ); + }, +}; diff --git a/backend/src/db/seeders/20250618165354.js b/backend/src/db/seeders/20250618165354.js new file mode 100644 index 0000000..01ce861 --- /dev/null +++ b/backend/src/db/seeders/20250618165354.js @@ -0,0 +1,87 @@ +const { v4: uuid } = require('uuid'); +const db = require('../models'); +const Sequelize = require('sequelize'); +const config = require('../../config'); + +module.exports = { + /** + * @param{import("sequelize").QueryInterface} queryInterface + * @return {Promise} + */ + async up(queryInterface) { + const createdAt = new Date(); + const updatedAt = new Date(); + + /** @type {Map} */ + const idMap = new Map(); + + /** + * @param {string} key + * @return {string} + */ + function getId(key) { + if (idMap.has(key)) { + return idMap.get(key); + } + const id = uuid(); + idMap.set(key, id); + return id; + } + + /** + * @param {string} name + */ + function createPermissions(name) { + return [ + { + id: getId(`CREATE_${name.toUpperCase()}`), + createdAt, + updatedAt, + name: `CREATE_${name.toUpperCase()}`, + }, + { + id: getId(`READ_${name.toUpperCase()}`), + createdAt, + updatedAt, + name: `READ_${name.toUpperCase()}`, + }, + { + id: getId(`UPDATE_${name.toUpperCase()}`), + createdAt, + updatedAt, + name: `UPDATE_${name.toUpperCase()}`, + }, + { + id: getId(`DELETE_${name.toUpperCase()}`), + createdAt, + updatedAt, + name: `DELETE_${name.toUpperCase()}`, + }, + ]; + } + + const entities = ['chatroomparticipants']; + + const createdPermissions = entities.flatMap(createPermissions); + + // Add permissions to database + await queryInterface.bulkInsert('permissions', createdPermissions); + // Get permissions ids + const permissionsIds = createdPermissions.map((p) => p.id); + // Get admin role + const adminRole = await db.roles.findOne({ + where: { name: config.roles.admin }, + }); + + if (adminRole) { + // Add permissions to admin role if it exists + await adminRole.addPermissions(permissionsIds); + } + }, + down: async (queryInterface, Sequelize) => { + await queryInterface.bulkDelete( + 'permissions', + entities.flatMap(createPermissions), + ); + }, +}; diff --git a/backend/src/index.js b/backend/src/index.js index 91c52e2..dff70b2 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -29,6 +29,12 @@ const rolesRoutes = require('./routes/roles'); const permissionsRoutes = require('./routes/permissions'); +const chatroomsRoutes = require('./routes/chatrooms'); + +const chatmessagesRoutes = require('./routes/chatmessages'); + +const chatroomparticipantsRoutes = require('./routes/chatroomparticipants'); + const getBaseUrl = (url) => { if (!url) return ''; return url.endsWith('/api') ? url.slice(0, -4) : url; @@ -39,9 +45,9 @@ const options = { openapi: '3.0.0', info: { version: '1.0.0', - title: 'Adminpro', + title: 'society-community-portal', description: - 'Adminpro Online REST API for Testing and Prototyping application. You can perform all major operations with your entities - create, delete and etc.', + 'society-community-portal Online REST API for Testing and Prototyping application. You can perform all major operations with your entities - create, delete and etc.', }, servers: [ { @@ -124,6 +130,24 @@ app.use( permissionsRoutes, ); +app.use( + '/api/chatrooms', + passport.authenticate('jwt', { session: false }), + chatroomsRoutes, +); + +app.use( + '/api/chatmessages', + passport.authenticate('jwt', { session: false }), + chatmessagesRoutes, +); + +app.use( + '/api/chatroomparticipants', + passport.authenticate('jwt', { session: false }), + chatroomparticipantsRoutes, +); + app.use( '/api/openai', passport.authenticate('jwt', { session: false }), diff --git a/backend/src/routes/chatmessages.js b/backend/src/routes/chatmessages.js new file mode 100644 index 0000000..679a37d --- /dev/null +++ b/backend/src/routes/chatmessages.js @@ -0,0 +1,442 @@ +const express = require('express'); + +const ChatmessagesService = require('../services/chatmessages'); +const ChatmessagesDBApi = require('../db/api/chatmessages'); +const wrapAsync = require('../helpers').wrapAsync; + +const router = express.Router(); + +const { parse } = require('json2csv'); + +const { checkCrudPermissions } = require('../middlewares/check-permissions'); + +router.use(checkCrudPermissions('chatmessages')); + +/** + * @swagger + * components: + * schemas: + * Chatmessages: + * type: object + * properties: + + * content: + * type: string + * default: content + + */ + +/** + * @swagger + * tags: + * name: Chatmessages + * description: The Chatmessages managing API + */ + +/** + * @swagger + * /api/chatmessages: + * post: + * security: + * - bearerAuth: [] + * tags: [Chatmessages] + * summary: Add new item + * description: Add new item + * requestBody: + * required: true + * content: + * application/json: + * schema: + * properties: + * data: + * description: Data of the updated item + * type: object + * $ref: "#/components/schemas/Chatmessages" + * responses: + * 200: + * description: The item was successfully added + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Chatmessages" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 405: + * description: Invalid input data + * 500: + * description: Some server error + */ +router.post( + '/', + wrapAsync(async (req, res) => { + const referer = + req.headers.referer || + `${req.protocol}://${req.hostname}${req.originalUrl}`; + const link = new URL(referer); + await ChatmessagesService.create( + req.body.data, + req.currentUser, + true, + link.host, + ); + const payload = true; + res.status(200).send(payload); + }), +); + +/** + * @swagger + * /api/budgets/bulk-import: + * post: + * security: + * - bearerAuth: [] + * tags: [Chatmessages] + * summary: Bulk import items + * description: Bulk import items + * requestBody: + * required: true + * content: + * application/json: + * schema: + * properties: + * data: + * description: Data of the updated items + * type: array + * items: + * $ref: "#/components/schemas/Chatmessages" + * responses: + * 200: + * description: The items were successfully imported + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Chatmessages" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 405: + * description: Invalid input data + * 500: + * description: Some server error + * + */ +router.post( + '/bulk-import', + wrapAsync(async (req, res) => { + const referer = + req.headers.referer || + `${req.protocol}://${req.hostname}${req.originalUrl}`; + const link = new URL(referer); + await ChatmessagesService.bulkImport(req, res, true, link.host); + const payload = true; + res.status(200).send(payload); + }), +); + +/** + * @swagger + * /api/chatmessages/{id}: + * put: + * security: + * - bearerAuth: [] + * tags: [Chatmessages] + * summary: Update the data of the selected item + * description: Update the data of the selected item + * parameters: + * - in: path + * name: id + * description: Item ID to update + * required: true + * schema: + * type: string + * requestBody: + * description: Set new item data + * required: true + * content: + * application/json: + * schema: + * properties: + * id: + * description: ID of the updated item + * type: string + * data: + * description: Data of the updated item + * type: object + * $ref: "#/components/schemas/Chatmessages" + * required: + * - id + * responses: + * 200: + * description: The item data was successfully updated + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Chatmessages" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.put( + '/:id', + wrapAsync(async (req, res) => { + await ChatmessagesService.update( + req.body.data, + req.body.id, + req.currentUser, + ); + const payload = true; + res.status(200).send(payload); + }), +); + +/** + * @swagger + * /api/chatmessages/{id}: + * delete: + * security: + * - bearerAuth: [] + * tags: [Chatmessages] + * summary: Delete the selected item + * description: Delete the selected item + * parameters: + * - in: path + * name: id + * description: Item ID to delete + * required: true + * schema: + * type: string + * responses: + * 200: + * description: The item was successfully deleted + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Chatmessages" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.delete( + '/:id', + wrapAsync(async (req, res) => { + await ChatmessagesService.remove(req.params.id, req.currentUser); + const payload = true; + res.status(200).send(payload); + }), +); + +/** + * @swagger + * /api/chatmessages/deleteByIds: + * post: + * security: + * - bearerAuth: [] + * tags: [Chatmessages] + * summary: Delete the selected item list + * description: Delete the selected item list + * requestBody: + * required: true + * content: + * application/json: + * schema: + * properties: + * ids: + * description: IDs of the updated items + * type: array + * responses: + * 200: + * description: The items was successfully deleted + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Chatmessages" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Items not found + * 500: + * description: Some server error + */ +router.post( + '/deleteByIds', + wrapAsync(async (req, res) => { + await ChatmessagesService.deleteByIds(req.body.data, req.currentUser); + const payload = true; + res.status(200).send(payload); + }), +); + +/** + * @swagger + * /api/chatmessages: + * get: + * security: + * - bearerAuth: [] + * tags: [Chatmessages] + * summary: Get all chatmessages + * description: Get all chatmessages + * responses: + * 200: + * description: Chatmessages list successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Chatmessages" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error + */ +router.get( + '/', + wrapAsync(async (req, res) => { + const filetype = req.query.filetype; + + const currentUser = req.currentUser; + const payload = await ChatmessagesDBApi.findAll(req.query, { currentUser }); + if (filetype && filetype === 'csv') { + const fields = ['id', 'content', 'created_date']; + const opts = { fields }; + try { + const csv = parse(payload.rows, opts); + res.status(200).attachment(csv); + res.send(csv); + } catch (err) { + console.error(err); + } + } else { + res.status(200).send(payload); + } + }), +); + +/** + * @swagger + * /api/chatmessages/count: + * get: + * security: + * - bearerAuth: [] + * tags: [Chatmessages] + * summary: Count all chatmessages + * description: Count all chatmessages + * responses: + * 200: + * description: Chatmessages count successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Chatmessages" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error + */ +router.get( + '/count', + wrapAsync(async (req, res) => { + const currentUser = req.currentUser; + const payload = await ChatmessagesDBApi.findAll(req.query, null, { + countOnly: true, + currentUser, + }); + + res.status(200).send(payload); + }), +); + +/** + * @swagger + * /api/chatmessages/autocomplete: + * get: + * security: + * - bearerAuth: [] + * tags: [Chatmessages] + * summary: Find all chatmessages that match search criteria + * description: Find all chatmessages that match search criteria + * responses: + * 200: + * description: Chatmessages list successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Chatmessages" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error + */ +router.get('/autocomplete', async (req, res) => { + const payload = await ChatmessagesDBApi.findAllAutocomplete( + req.query.query, + req.query.limit, + req.query.offset, + ); + + res.status(200).send(payload); +}); + +/** + * @swagger + * /api/chatmessages/{id}: + * get: + * security: + * - bearerAuth: [] + * tags: [Chatmessages] + * summary: Get selected item + * description: Get selected item + * parameters: + * - in: path + * name: id + * description: ID of item to get + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Selected item successfully received + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Chatmessages" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.get( + '/:id', + wrapAsync(async (req, res) => { + const payload = await ChatmessagesDBApi.findBy({ id: req.params.id }); + + res.status(200).send(payload); + }), +); + +router.use('/', require('../helpers').commonErrorHandler); + +module.exports = router; diff --git a/backend/src/routes/chatroomparticipants.js b/backend/src/routes/chatroomparticipants.js new file mode 100644 index 0000000..5806459 --- /dev/null +++ b/backend/src/routes/chatroomparticipants.js @@ -0,0 +1,445 @@ +const express = require('express'); + +const ChatroomparticipantsService = require('../services/chatroomparticipants'); +const ChatroomparticipantsDBApi = require('../db/api/chatroomparticipants'); +const wrapAsync = require('../helpers').wrapAsync; + +const router = express.Router(); + +const { parse } = require('json2csv'); + +const { checkCrudPermissions } = require('../middlewares/check-permissions'); + +router.use(checkCrudPermissions('chatroomparticipants')); + +/** + * @swagger + * components: + * schemas: + * Chatroomparticipants: + * type: object + * properties: + + */ + +/** + * @swagger + * tags: + * name: Chatroomparticipants + * description: The Chatroomparticipants managing API + */ + +/** + * @swagger + * /api/chatroomparticipants: + * post: + * security: + * - bearerAuth: [] + * tags: [Chatroomparticipants] + * summary: Add new item + * description: Add new item + * requestBody: + * required: true + * content: + * application/json: + * schema: + * properties: + * data: + * description: Data of the updated item + * type: object + * $ref: "#/components/schemas/Chatroomparticipants" + * responses: + * 200: + * description: The item was successfully added + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Chatroomparticipants" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 405: + * description: Invalid input data + * 500: + * description: Some server error + */ +router.post( + '/', + wrapAsync(async (req, res) => { + const referer = + req.headers.referer || + `${req.protocol}://${req.hostname}${req.originalUrl}`; + const link = new URL(referer); + await ChatroomparticipantsService.create( + req.body.data, + req.currentUser, + true, + link.host, + ); + const payload = true; + res.status(200).send(payload); + }), +); + +/** + * @swagger + * /api/budgets/bulk-import: + * post: + * security: + * - bearerAuth: [] + * tags: [Chatroomparticipants] + * summary: Bulk import items + * description: Bulk import items + * requestBody: + * required: true + * content: + * application/json: + * schema: + * properties: + * data: + * description: Data of the updated items + * type: array + * items: + * $ref: "#/components/schemas/Chatroomparticipants" + * responses: + * 200: + * description: The items were successfully imported + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Chatroomparticipants" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 405: + * description: Invalid input data + * 500: + * description: Some server error + * + */ +router.post( + '/bulk-import', + wrapAsync(async (req, res) => { + const referer = + req.headers.referer || + `${req.protocol}://${req.hostname}${req.originalUrl}`; + const link = new URL(referer); + await ChatroomparticipantsService.bulkImport(req, res, true, link.host); + const payload = true; + res.status(200).send(payload); + }), +); + +/** + * @swagger + * /api/chatroomparticipants/{id}: + * put: + * security: + * - bearerAuth: [] + * tags: [Chatroomparticipants] + * summary: Update the data of the selected item + * description: Update the data of the selected item + * parameters: + * - in: path + * name: id + * description: Item ID to update + * required: true + * schema: + * type: string + * requestBody: + * description: Set new item data + * required: true + * content: + * application/json: + * schema: + * properties: + * id: + * description: ID of the updated item + * type: string + * data: + * description: Data of the updated item + * type: object + * $ref: "#/components/schemas/Chatroomparticipants" + * required: + * - id + * responses: + * 200: + * description: The item data was successfully updated + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Chatroomparticipants" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.put( + '/:id', + wrapAsync(async (req, res) => { + await ChatroomparticipantsService.update( + req.body.data, + req.body.id, + req.currentUser, + ); + const payload = true; + res.status(200).send(payload); + }), +); + +/** + * @swagger + * /api/chatroomparticipants/{id}: + * delete: + * security: + * - bearerAuth: [] + * tags: [Chatroomparticipants] + * summary: Delete the selected item + * description: Delete the selected item + * parameters: + * - in: path + * name: id + * description: Item ID to delete + * required: true + * schema: + * type: string + * responses: + * 200: + * description: The item was successfully deleted + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Chatroomparticipants" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.delete( + '/:id', + wrapAsync(async (req, res) => { + await ChatroomparticipantsService.remove(req.params.id, req.currentUser); + const payload = true; + res.status(200).send(payload); + }), +); + +/** + * @swagger + * /api/chatroomparticipants/deleteByIds: + * post: + * security: + * - bearerAuth: [] + * tags: [Chatroomparticipants] + * summary: Delete the selected item list + * description: Delete the selected item list + * requestBody: + * required: true + * content: + * application/json: + * schema: + * properties: + * ids: + * description: IDs of the updated items + * type: array + * responses: + * 200: + * description: The items was successfully deleted + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Chatroomparticipants" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Items not found + * 500: + * description: Some server error + */ +router.post( + '/deleteByIds', + wrapAsync(async (req, res) => { + await ChatroomparticipantsService.deleteByIds( + req.body.data, + req.currentUser, + ); + const payload = true; + res.status(200).send(payload); + }), +); + +/** + * @swagger + * /api/chatroomparticipants: + * get: + * security: + * - bearerAuth: [] + * tags: [Chatroomparticipants] + * summary: Get all chatroomparticipants + * description: Get all chatroomparticipants + * responses: + * 200: + * description: Chatroomparticipants list successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Chatroomparticipants" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error + */ +router.get( + '/', + wrapAsync(async (req, res) => { + const filetype = req.query.filetype; + + const currentUser = req.currentUser; + const payload = await ChatroomparticipantsDBApi.findAll(req.query, { + currentUser, + }); + if (filetype && filetype === 'csv') { + const fields = ['id']; + const opts = { fields }; + try { + const csv = parse(payload.rows, opts); + res.status(200).attachment(csv); + res.send(csv); + } catch (err) { + console.error(err); + } + } else { + res.status(200).send(payload); + } + }), +); + +/** + * @swagger + * /api/chatroomparticipants/count: + * get: + * security: + * - bearerAuth: [] + * tags: [Chatroomparticipants] + * summary: Count all chatroomparticipants + * description: Count all chatroomparticipants + * responses: + * 200: + * description: Chatroomparticipants count successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Chatroomparticipants" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error + */ +router.get( + '/count', + wrapAsync(async (req, res) => { + const currentUser = req.currentUser; + const payload = await ChatroomparticipantsDBApi.findAll(req.query, null, { + countOnly: true, + currentUser, + }); + + res.status(200).send(payload); + }), +); + +/** + * @swagger + * /api/chatroomparticipants/autocomplete: + * get: + * security: + * - bearerAuth: [] + * tags: [Chatroomparticipants] + * summary: Find all chatroomparticipants that match search criteria + * description: Find all chatroomparticipants that match search criteria + * responses: + * 200: + * description: Chatroomparticipants list successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Chatroomparticipants" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error + */ +router.get('/autocomplete', async (req, res) => { + const payload = await ChatroomparticipantsDBApi.findAllAutocomplete( + req.query.query, + req.query.limit, + req.query.offset, + ); + + res.status(200).send(payload); +}); + +/** + * @swagger + * /api/chatroomparticipants/{id}: + * get: + * security: + * - bearerAuth: [] + * tags: [Chatroomparticipants] + * summary: Get selected item + * description: Get selected item + * parameters: + * - in: path + * name: id + * description: ID of item to get + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Selected item successfully received + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Chatroomparticipants" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.get( + '/:id', + wrapAsync(async (req, res) => { + const payload = await ChatroomparticipantsDBApi.findBy({ + id: req.params.id, + }); + + res.status(200).send(payload); + }), +); + +router.use('/', require('../helpers').commonErrorHandler); + +module.exports = router; diff --git a/backend/src/routes/chatrooms.js b/backend/src/routes/chatrooms.js new file mode 100644 index 0000000..add4fa7 --- /dev/null +++ b/backend/src/routes/chatrooms.js @@ -0,0 +1,439 @@ +const express = require('express'); + +const ChatroomsService = require('../services/chatrooms'); +const ChatroomsDBApi = require('../db/api/chatrooms'); +const wrapAsync = require('../helpers').wrapAsync; + +const router = express.Router(); + +const { parse } = require('json2csv'); + +const { checkCrudPermissions } = require('../middlewares/check-permissions'); + +router.use(checkCrudPermissions('chatrooms')); + +/** + * @swagger + * components: + * schemas: + * Chatrooms: + * type: object + * properties: + + * name: + * type: string + * default: name + + * + */ + +/** + * @swagger + * tags: + * name: Chatrooms + * description: The Chatrooms managing API + */ + +/** + * @swagger + * /api/chatrooms: + * post: + * security: + * - bearerAuth: [] + * tags: [Chatrooms] + * summary: Add new item + * description: Add new item + * requestBody: + * required: true + * content: + * application/json: + * schema: + * properties: + * data: + * description: Data of the updated item + * type: object + * $ref: "#/components/schemas/Chatrooms" + * responses: + * 200: + * description: The item was successfully added + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Chatrooms" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 405: + * description: Invalid input data + * 500: + * description: Some server error + */ +router.post( + '/', + wrapAsync(async (req, res) => { + const referer = + req.headers.referer || + `${req.protocol}://${req.hostname}${req.originalUrl}`; + const link = new URL(referer); + await ChatroomsService.create( + req.body.data, + req.currentUser, + true, + link.host, + ); + const payload = true; + res.status(200).send(payload); + }), +); + +/** + * @swagger + * /api/budgets/bulk-import: + * post: + * security: + * - bearerAuth: [] + * tags: [Chatrooms] + * summary: Bulk import items + * description: Bulk import items + * requestBody: + * required: true + * content: + * application/json: + * schema: + * properties: + * data: + * description: Data of the updated items + * type: array + * items: + * $ref: "#/components/schemas/Chatrooms" + * responses: + * 200: + * description: The items were successfully imported + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Chatrooms" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 405: + * description: Invalid input data + * 500: + * description: Some server error + * + */ +router.post( + '/bulk-import', + wrapAsync(async (req, res) => { + const referer = + req.headers.referer || + `${req.protocol}://${req.hostname}${req.originalUrl}`; + const link = new URL(referer); + await ChatroomsService.bulkImport(req, res, true, link.host); + const payload = true; + res.status(200).send(payload); + }), +); + +/** + * @swagger + * /api/chatrooms/{id}: + * put: + * security: + * - bearerAuth: [] + * tags: [Chatrooms] + * summary: Update the data of the selected item + * description: Update the data of the selected item + * parameters: + * - in: path + * name: id + * description: Item ID to update + * required: true + * schema: + * type: string + * requestBody: + * description: Set new item data + * required: true + * content: + * application/json: + * schema: + * properties: + * id: + * description: ID of the updated item + * type: string + * data: + * description: Data of the updated item + * type: object + * $ref: "#/components/schemas/Chatrooms" + * required: + * - id + * responses: + * 200: + * description: The item data was successfully updated + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Chatrooms" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.put( + '/:id', + wrapAsync(async (req, res) => { + await ChatroomsService.update(req.body.data, req.body.id, req.currentUser); + const payload = true; + res.status(200).send(payload); + }), +); + +/** + * @swagger + * /api/chatrooms/{id}: + * delete: + * security: + * - bearerAuth: [] + * tags: [Chatrooms] + * summary: Delete the selected item + * description: Delete the selected item + * parameters: + * - in: path + * name: id + * description: Item ID to delete + * required: true + * schema: + * type: string + * responses: + * 200: + * description: The item was successfully deleted + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Chatrooms" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.delete( + '/:id', + wrapAsync(async (req, res) => { + await ChatroomsService.remove(req.params.id, req.currentUser); + const payload = true; + res.status(200).send(payload); + }), +); + +/** + * @swagger + * /api/chatrooms/deleteByIds: + * post: + * security: + * - bearerAuth: [] + * tags: [Chatrooms] + * summary: Delete the selected item list + * description: Delete the selected item list + * requestBody: + * required: true + * content: + * application/json: + * schema: + * properties: + * ids: + * description: IDs of the updated items + * type: array + * responses: + * 200: + * description: The items was successfully deleted + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Chatrooms" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Items not found + * 500: + * description: Some server error + */ +router.post( + '/deleteByIds', + wrapAsync(async (req, res) => { + await ChatroomsService.deleteByIds(req.body.data, req.currentUser); + const payload = true; + res.status(200).send(payload); + }), +); + +/** + * @swagger + * /api/chatrooms: + * get: + * security: + * - bearerAuth: [] + * tags: [Chatrooms] + * summary: Get all chatrooms + * description: Get all chatrooms + * responses: + * 200: + * description: Chatrooms list successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Chatrooms" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error + */ +router.get( + '/', + wrapAsync(async (req, res) => { + const filetype = req.query.filetype; + + const currentUser = req.currentUser; + const payload = await ChatroomsDBApi.findAll(req.query, { currentUser }); + if (filetype && filetype === 'csv') { + const fields = ['id', 'name', 'created_date']; + const opts = { fields }; + try { + const csv = parse(payload.rows, opts); + res.status(200).attachment(csv); + res.send(csv); + } catch (err) { + console.error(err); + } + } else { + res.status(200).send(payload); + } + }), +); + +/** + * @swagger + * /api/chatrooms/count: + * get: + * security: + * - bearerAuth: [] + * tags: [Chatrooms] + * summary: Count all chatrooms + * description: Count all chatrooms + * responses: + * 200: + * description: Chatrooms count successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Chatrooms" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error + */ +router.get( + '/count', + wrapAsync(async (req, res) => { + const currentUser = req.currentUser; + const payload = await ChatroomsDBApi.findAll(req.query, null, { + countOnly: true, + currentUser, + }); + + res.status(200).send(payload); + }), +); + +/** + * @swagger + * /api/chatrooms/autocomplete: + * get: + * security: + * - bearerAuth: [] + * tags: [Chatrooms] + * summary: Find all chatrooms that match search criteria + * description: Find all chatrooms that match search criteria + * responses: + * 200: + * description: Chatrooms list successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Chatrooms" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error + */ +router.get('/autocomplete', async (req, res) => { + const payload = await ChatroomsDBApi.findAllAutocomplete( + req.query.query, + req.query.limit, + req.query.offset, + ); + + res.status(200).send(payload); +}); + +/** + * @swagger + * /api/chatrooms/{id}: + * get: + * security: + * - bearerAuth: [] + * tags: [Chatrooms] + * summary: Get selected item + * description: Get selected item + * parameters: + * - in: path + * name: id + * description: ID of item to get + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Selected item successfully received + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Chatrooms" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.get( + '/:id', + wrapAsync(async (req, res) => { + const payload = await ChatroomsDBApi.findBy({ id: req.params.id }); + + res.status(200).send(payload); + }), +); + +router.use('/', require('../helpers').commonErrorHandler); + +module.exports = router; diff --git a/backend/src/services/chatmessages.js b/backend/src/services/chatmessages.js new file mode 100644 index 0000000..0cd14ac --- /dev/null +++ b/backend/src/services/chatmessages.js @@ -0,0 +1,117 @@ +const db = require('../db/models'); +const ChatmessagesDBApi = require('../db/api/chatmessages'); +const processFile = require('../middlewares/upload'); +const ValidationError = require('./notifications/errors/validation'); +const csv = require('csv-parser'); +const axios = require('axios'); +const config = require('../config'); +const stream = require('stream'); + +module.exports = class ChatmessagesService { + static async create(data, currentUser) { + const transaction = await db.sequelize.transaction(); + try { + await ChatmessagesDBApi.create(data, { + currentUser, + transaction, + }); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + static async bulkImport(req, res, sendInvitationEmails = true, host) { + const transaction = await db.sequelize.transaction(); + + try { + await processFile(req, res); + const bufferStream = new stream.PassThrough(); + const results = []; + + await bufferStream.end(Buffer.from(req.file.buffer, 'utf-8')); // convert Buffer to Stream + + await new Promise((resolve, reject) => { + bufferStream + .pipe(csv()) + .on('data', (data) => results.push(data)) + .on('end', async () => { + console.log('CSV results', results); + resolve(); + }) + .on('error', (error) => reject(error)); + }); + + await ChatmessagesDBApi.bulkImport(results, { + transaction, + ignoreDuplicates: true, + validate: true, + currentUser: req.currentUser, + }); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + static async update(data, id, currentUser) { + const transaction = await db.sequelize.transaction(); + try { + let chatmessages = await ChatmessagesDBApi.findBy( + { id }, + { transaction }, + ); + + if (!chatmessages) { + throw new ValidationError('chatmessagesNotFound'); + } + + const updatedChatmessages = await ChatmessagesDBApi.update(id, data, { + currentUser, + transaction, + }); + + await transaction.commit(); + return updatedChatmessages; + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + static async deleteByIds(ids, currentUser) { + const transaction = await db.sequelize.transaction(); + + try { + await ChatmessagesDBApi.deleteByIds(ids, { + currentUser, + transaction, + }); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + static async remove(id, currentUser) { + const transaction = await db.sequelize.transaction(); + + try { + await ChatmessagesDBApi.remove(id, { + currentUser, + transaction, + }); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } +}; diff --git a/backend/src/services/chatroomparticipants.js b/backend/src/services/chatroomparticipants.js new file mode 100644 index 0000000..675b57f --- /dev/null +++ b/backend/src/services/chatroomparticipants.js @@ -0,0 +1,118 @@ +const db = require('../db/models'); +const ChatroomparticipantsDBApi = require('../db/api/chatroomparticipants'); +const processFile = require('../middlewares/upload'); +const ValidationError = require('./notifications/errors/validation'); +const csv = require('csv-parser'); +const axios = require('axios'); +const config = require('../config'); +const stream = require('stream'); + +module.exports = class ChatroomparticipantsService { + static async create(data, currentUser) { + const transaction = await db.sequelize.transaction(); + try { + await ChatroomparticipantsDBApi.create(data, { + currentUser, + transaction, + }); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + static async bulkImport(req, res, sendInvitationEmails = true, host) { + const transaction = await db.sequelize.transaction(); + + try { + await processFile(req, res); + const bufferStream = new stream.PassThrough(); + const results = []; + + await bufferStream.end(Buffer.from(req.file.buffer, 'utf-8')); // convert Buffer to Stream + + await new Promise((resolve, reject) => { + bufferStream + .pipe(csv()) + .on('data', (data) => results.push(data)) + .on('end', async () => { + console.log('CSV results', results); + resolve(); + }) + .on('error', (error) => reject(error)); + }); + + await ChatroomparticipantsDBApi.bulkImport(results, { + transaction, + ignoreDuplicates: true, + validate: true, + currentUser: req.currentUser, + }); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + static async update(data, id, currentUser) { + const transaction = await db.sequelize.transaction(); + try { + let chatroomparticipants = await ChatroomparticipantsDBApi.findBy( + { id }, + { transaction }, + ); + + if (!chatroomparticipants) { + throw new ValidationError('chatroomparticipantsNotFound'); + } + + const updatedChatroomparticipants = + await ChatroomparticipantsDBApi.update(id, data, { + currentUser, + transaction, + }); + + await transaction.commit(); + return updatedChatroomparticipants; + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + static async deleteByIds(ids, currentUser) { + const transaction = await db.sequelize.transaction(); + + try { + await ChatroomparticipantsDBApi.deleteByIds(ids, { + currentUser, + transaction, + }); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + static async remove(id, currentUser) { + const transaction = await db.sequelize.transaction(); + + try { + await ChatroomparticipantsDBApi.remove(id, { + currentUser, + transaction, + }); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } +}; diff --git a/backend/src/services/chatrooms.js b/backend/src/services/chatrooms.js new file mode 100644 index 0000000..428e211 --- /dev/null +++ b/backend/src/services/chatrooms.js @@ -0,0 +1,114 @@ +const db = require('../db/models'); +const ChatroomsDBApi = require('../db/api/chatrooms'); +const processFile = require('../middlewares/upload'); +const ValidationError = require('./notifications/errors/validation'); +const csv = require('csv-parser'); +const axios = require('axios'); +const config = require('../config'); +const stream = require('stream'); + +module.exports = class ChatroomsService { + static async create(data, currentUser) { + const transaction = await db.sequelize.transaction(); + try { + await ChatroomsDBApi.create(data, { + currentUser, + transaction, + }); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + static async bulkImport(req, res, sendInvitationEmails = true, host) { + const transaction = await db.sequelize.transaction(); + + try { + await processFile(req, res); + const bufferStream = new stream.PassThrough(); + const results = []; + + await bufferStream.end(Buffer.from(req.file.buffer, 'utf-8')); // convert Buffer to Stream + + await new Promise((resolve, reject) => { + bufferStream + .pipe(csv()) + .on('data', (data) => results.push(data)) + .on('end', async () => { + console.log('CSV results', results); + resolve(); + }) + .on('error', (error) => reject(error)); + }); + + await ChatroomsDBApi.bulkImport(results, { + transaction, + ignoreDuplicates: true, + validate: true, + currentUser: req.currentUser, + }); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + static async update(data, id, currentUser) { + const transaction = await db.sequelize.transaction(); + try { + let chatrooms = await ChatroomsDBApi.findBy({ id }, { transaction }); + + if (!chatrooms) { + throw new ValidationError('chatroomsNotFound'); + } + + const updatedChatrooms = await ChatroomsDBApi.update(id, data, { + currentUser, + transaction, + }); + + await transaction.commit(); + return updatedChatrooms; + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + static async deleteByIds(ids, currentUser) { + const transaction = await db.sequelize.transaction(); + + try { + await ChatroomsDBApi.deleteByIds(ids, { + currentUser, + transaction, + }); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + static async remove(id, currentUser) { + const transaction = await db.sequelize.transaction(); + + try { + await ChatroomsDBApi.remove(id, { + currentUser, + transaction, + }); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } +}; diff --git a/backend/src/services/notifications/list.js b/backend/src/services/notifications/list.js index 9a363a7..77a5101 100644 --- a/backend/src/services/notifications/list.js +++ b/backend/src/services/notifications/list.js @@ -1,6 +1,6 @@ const errors = { app: { - title: 'Adminpro', + title: 'society-community-portal', }, auth: { diff --git a/backend/src/services/search.js b/backend/src/services/search.js index 05fd805..18ba9cd 100644 --- a/backend/src/services/search.js +++ b/backend/src/services/search.js @@ -46,6 +46,10 @@ module.exports = class SearchService { departments: ['name'], reports: ['title'], + + chatrooms: ['name'], + + chatmessages: ['content'], }; const columnsInt = {}; diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 6c9b041..6215971 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -25,7 +25,7 @@ services: - ./data/db:/var/lib/postgresql/data environment: - POSTGRES_HOST_AUTH_METHOD=trust - - POSTGRES_DB=db_adminpro + - POSTGRES_DB=db_society_community_portal ports: - "5432:5432" logging: diff --git a/frontend/README.md b/frontend/README.md index c36bd6b..180586d 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,4 +1,4 @@ -# Adminpro +# society-community-portal ## This project was generated by Flatlogic Platform. diff --git a/frontend/src/components/AsideMenuLayer.tsx b/frontend/src/components/AsideMenuLayer.tsx index 4008b92..6e6213c 100644 --- a/frontend/src/components/AsideMenuLayer.tsx +++ b/frontend/src/components/AsideMenuLayer.tsx @@ -45,7 +45,7 @@ export default function AsideMenuLayer({ >
- Adminpro + society-community-portal
)} + + {hasPermission(currentUser, 'READ_CHATROOMS') && ( + +
+
+
+
+ Chatrooms +
+
+ {chatrooms} +
+
+
+ +
+
+
+ + )} + + {hasPermission(currentUser, 'READ_CHATMESSAGES') && ( + +
+
+
+
+ Chatmessages +
+
+ {chatmessages} +
+
+
+ +
+
+
+ + )} + + {hasPermission(currentUser, 'READ_CHATROOMPARTICIPANTS') && ( + +
+
+
+
+ Chatroomparticipants +
+
+ {chatroomparticipants} +
+
+
+ +
+
+
+ + )} diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx index a488d32..8877556 100644 --- a/frontend/src/pages/index.tsx +++ b/frontend/src/pages/index.tsx @@ -27,7 +27,7 @@ import FaqSection from '../components/WebPageComponents/FaqComponent'; export default function WebSite() { const cardsStyle = useAppSelector((state) => state.style.cardsStyle); const bgColor = useAppSelector((state) => state.style.bgLayoutColor); - const projectName = 'Adminpro'; + const projectName = 'society-community-portal'; useEffect(() => { const darkElement = document.querySelector('body .dark'); @@ -125,10 +125,10 @@ export default function WebSite() { content={`Discover our comprehensive admin portal designed for management teams, offering robust features, seamless communication, and efficient data management.`} /> - +
- + ); } diff --git a/frontend/src/pages/login.tsx b/frontend/src/pages/login.tsx index ff37b2d..3c185a1 100644 --- a/frontend/src/pages/login.tsx +++ b/frontend/src/pages/login.tsx @@ -52,7 +52,7 @@ export default function Login() { remember: true, }); - const title = 'Adminpro'; + const title = 'society-community-portal'; // Fetch Pexels image/video useEffect(() => { diff --git a/frontend/src/pages/privacy-policy.tsx b/frontend/src/pages/privacy-policy.tsx index 473508f..09744bc 100644 --- a/frontend/src/pages/privacy-policy.tsx +++ b/frontend/src/pages/privacy-policy.tsx @@ -5,7 +5,7 @@ import LayoutGuest from '../layouts/Guest'; import { getPageTitle } from '../config'; export default function PrivacyPolicy() { - const title = 'Adminpro'; + const title = 'society-community-portal'; const [projectUrl, setProjectUrl] = useState(''); useEffect(() => { diff --git a/frontend/src/pages/terms-of-use.tsx b/frontend/src/pages/terms-of-use.tsx index 41a1fd8..ebb6c97 100644 --- a/frontend/src/pages/terms-of-use.tsx +++ b/frontend/src/pages/terms-of-use.tsx @@ -5,7 +5,7 @@ import LayoutGuest from '../layouts/Guest'; import { getPageTitle } from '../config'; export default function PrivacyPolicy() { - const title = 'Adminpro'; + const title = 'society-community-portal'; const [projectUrl, setProjectUrl] = useState(''); useEffect(() => { diff --git a/frontend/src/pages/users/users-view.tsx b/frontend/src/pages/users/users-view.tsx index ce0851d..1c5f887 100644 --- a/frontend/src/pages/users/users-view.tsx +++ b/frontend/src/pages/users/users-view.tsx @@ -138,6 +138,82 @@ const UsersView = () => { + <> +

Chatmessages Sender

+ +
+ + + + + + + + + + {users.chatmessages_sender && + Array.isArray(users.chatmessages_sender) && + users.chatmessages_sender.map((item: any) => ( + + router.push( + `/chatmessages/chatmessages-view/?id=${item.id}`, + ) + } + > + + + + + ))} + +
ContentCreated date
{item.content} + {dataFormatter.dateTimeFormatter(item.created_date)} +
+
+ {!users?.chatmessages_sender?.length && ( +
No data
+ )} +
+ + + <> +

Chatroomparticipants User

+ +
+ + + + + + {users.chatroomparticipants_user && + Array.isArray(users.chatroomparticipants_user) && + users.chatroomparticipants_user.map((item: any) => ( + + router.push( + `/chatroomparticipants/chatroomparticipants-view/?id=${item.id}`, + ) + } + > + ))} + +
+
+ {!users?.chatroomparticipants_user?.length && ( +
No data
+ )} +
+ + state.style.cardsStyle); const bgColor = useAppSelector((state) => state.style.bgLayoutColor); - const projectName = 'Adminpro'; + const projectName = 'society-community-portal'; useEffect(() => { const darkElement = document.querySelector('body .dark'); @@ -96,10 +96,10 @@ export default function WebSite() { content={`Learn more about ${projectName}, our mission, values, and the innovative features that empower management teams to excel.`} /> - +
- + ); } diff --git a/frontend/src/pages/web_pages/contact.tsx b/frontend/src/pages/web_pages/contact.tsx index ada456a..8b030ce 100644 --- a/frontend/src/pages/web_pages/contact.tsx +++ b/frontend/src/pages/web_pages/contact.tsx @@ -21,7 +21,7 @@ import FaqSection from '../../components/WebPageComponents/FaqComponent'; export default function WebSite() { const cardsStyle = useAppSelector((state) => state.style.cardsStyle); const bgColor = useAppSelector((state) => state.style.bgLayoutColor); - const projectName = 'Adminpro'; + const projectName = 'society-community-portal'; useEffect(() => { const darkElement = document.querySelector('body .dark'); @@ -67,10 +67,10 @@ export default function WebSite() { content={`Get in touch with the ${projectName} team for inquiries, support, or feedback. We're here to assist you with any questions you may have.`} /> - +
- + ); } diff --git a/frontend/src/pages/web_pages/faq.tsx b/frontend/src/pages/web_pages/faq.tsx index 1a8c960..9dde8d3 100644 --- a/frontend/src/pages/web_pages/faq.tsx +++ b/frontend/src/pages/web_pages/faq.tsx @@ -18,7 +18,7 @@ import FaqSection from '../../components/WebPageComponents/FaqComponent'; export default function WebSite() { const cardsStyle = useAppSelector((state) => state.style.cardsStyle); const bgColor = useAppSelector((state) => state.style.bgLayoutColor); - const projectName = 'Adminpro'; + const projectName = 'society-community-portal'; useEffect(() => { const darkElement = document.querySelector('body .dark'); @@ -69,10 +69,10 @@ export default function WebSite() { content={`Find answers to common questions about ${projectName}. Learn more about our features, pricing, and support options.`} /> - +
- + ); } diff --git a/frontend/src/pages/web_pages/home.tsx b/frontend/src/pages/web_pages/home.tsx index 162eeb5..1b01664 100644 --- a/frontend/src/pages/web_pages/home.tsx +++ b/frontend/src/pages/web_pages/home.tsx @@ -27,7 +27,7 @@ import FaqSection from '../../components/WebPageComponents/FaqComponent'; export default function WebSite() { const cardsStyle = useAppSelector((state) => state.style.cardsStyle); const bgColor = useAppSelector((state) => state.style.bgLayoutColor); - const projectName = 'Adminpro'; + const projectName = 'society-community-portal'; useEffect(() => { const darkElement = document.querySelector('body .dark'); @@ -99,10 +99,10 @@ export default function WebSite() { content={`Discover our comprehensive admin portal designed for management teams, offering robust features, seamless communication, and efficient data management.`} /> - +
- + ); } diff --git a/frontend/src/pages/web_pages/services.tsx b/frontend/src/pages/web_pages/services.tsx index e7f956c..55b3b61 100644 --- a/frontend/src/pages/web_pages/services.tsx +++ b/frontend/src/pages/web_pages/services.tsx @@ -21,7 +21,7 @@ import FaqSection from '../../components/WebPageComponents/FaqComponent'; export default function WebSite() { const cardsStyle = useAppSelector((state) => state.style.cardsStyle); const bgColor = useAppSelector((state) => state.style.bgLayoutColor); - const projectName = 'Adminpro'; + const projectName = 'society-community-portal'; useEffect(() => { const darkElement = document.querySelector('body .dark'); @@ -88,10 +88,10 @@ export default function WebSite() { content={`Explore the range of services offered by ${projectName}, designed to enhance management efficiency and drive team success.`} /> - +
- + ); } diff --git a/frontend/src/stores/chatmessages/chatmessagesSlice.ts b/frontend/src/stores/chatmessages/chatmessagesSlice.ts new file mode 100644 index 0000000..a9806d4 --- /dev/null +++ b/frontend/src/stores/chatmessages/chatmessagesSlice.ts @@ -0,0 +1,241 @@ +import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit'; +import axios from 'axios'; +import { + fulfilledNotify, + rejectNotify, + resetNotify, +} from '../../helpers/notifyStateHandler'; + +interface MainState { + chatmessages: any; + loading: boolean; + count: number; + refetch: boolean; + rolesWidgets: any[]; + notify: { + showNotification: boolean; + textNotification: string; + typeNotification: string; + }; +} + +const initialState: MainState = { + chatmessages: [], + loading: false, + count: 0, + refetch: false, + rolesWidgets: [], + notify: { + showNotification: false, + textNotification: '', + typeNotification: 'warn', + }, +}; + +export const fetch = createAsyncThunk( + 'chatmessages/fetch', + async (data: any) => { + const { id, query } = data; + const result = await axios.get( + `chatmessages${query || (id ? `/${id}` : '')}`, + ); + return id + ? result.data + : { rows: result.data.rows, count: result.data.count }; + }, +); + +export const deleteItemsByIds = createAsyncThunk( + 'chatmessages/deleteByIds', + async (data: any, { rejectWithValue }) => { + try { + await axios.post('chatmessages/deleteByIds', { data }); + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } + }, +); + +export const deleteItem = createAsyncThunk( + 'chatmessages/deleteChatmessages', + async (id: string, { rejectWithValue }) => { + try { + await axios.delete(`chatmessages/${id}`); + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } + }, +); + +export const create = createAsyncThunk( + 'chatmessages/createChatmessages', + async (data: any, { rejectWithValue }) => { + try { + const result = await axios.post('chatmessages', { data }); + return result.data; + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } + }, +); + +export const uploadCsv = createAsyncThunk( + 'chatmessages/uploadCsv', + async (file: File, { rejectWithValue }) => { + try { + const data = new FormData(); + data.append('file', file); + data.append('filename', file.name); + + const result = await axios.post('chatmessages/bulk-import', data, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + + return result.data; + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } + }, +); + +export const update = createAsyncThunk( + 'chatmessages/updateChatmessages', + async (payload: any, { rejectWithValue }) => { + try { + const result = await axios.put(`chatmessages/${payload.id}`, { + id: payload.id, + data: payload.data, + }); + return result.data; + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } + }, +); + +export const chatmessagesSlice = createSlice({ + name: 'chatmessages', + initialState, + reducers: { + setRefetch: (state, action: PayloadAction) => { + state.refetch = action.payload; + }, + }, + extraReducers: (builder) => { + builder.addCase(fetch.pending, (state) => { + state.loading = true; + resetNotify(state); + }); + builder.addCase(fetch.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }); + + builder.addCase(fetch.fulfilled, (state, action) => { + if (action.payload.rows && action.payload.count >= 0) { + state.chatmessages = action.payload.rows; + state.count = action.payload.count; + } else { + state.chatmessages = action.payload; + } + state.loading = false; + }); + + builder.addCase(deleteItemsByIds.pending, (state) => { + state.loading = true; + resetNotify(state); + }); + + builder.addCase(deleteItemsByIds.fulfilled, (state) => { + state.loading = false; + fulfilledNotify(state, 'Chatmessages has been deleted'); + }); + + builder.addCase(deleteItemsByIds.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }); + + builder.addCase(deleteItem.pending, (state) => { + state.loading = true; + resetNotify(state); + }); + + builder.addCase(deleteItem.fulfilled, (state) => { + state.loading = false; + fulfilledNotify(state, `${'Chatmessages'.slice(0, -1)} has been deleted`); + }); + + builder.addCase(deleteItem.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }); + + builder.addCase(create.pending, (state) => { + state.loading = true; + resetNotify(state); + }); + builder.addCase(create.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }); + + builder.addCase(create.fulfilled, (state) => { + state.loading = false; + fulfilledNotify(state, `${'Chatmessages'.slice(0, -1)} has been created`); + }); + + builder.addCase(update.pending, (state) => { + state.loading = true; + resetNotify(state); + }); + builder.addCase(update.fulfilled, (state) => { + state.loading = false; + fulfilledNotify(state, `${'Chatmessages'.slice(0, -1)} has been updated`); + }); + builder.addCase(update.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }); + + builder.addCase(uploadCsv.pending, (state) => { + state.loading = true; + resetNotify(state); + }); + builder.addCase(uploadCsv.fulfilled, (state) => { + state.loading = false; + fulfilledNotify(state, 'Chatmessages has been uploaded'); + }); + builder.addCase(uploadCsv.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }); + }, +}); + +// Action creators are generated for each case reducer function +export const { setRefetch } = chatmessagesSlice.actions; + +export default chatmessagesSlice.reducer; diff --git a/frontend/src/stores/chatroomparticipants/chatroomparticipantsSlice.ts b/frontend/src/stores/chatroomparticipants/chatroomparticipantsSlice.ts new file mode 100644 index 0000000..c7cfdb9 --- /dev/null +++ b/frontend/src/stores/chatroomparticipants/chatroomparticipantsSlice.ts @@ -0,0 +1,254 @@ +import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit'; +import axios from 'axios'; +import { + fulfilledNotify, + rejectNotify, + resetNotify, +} from '../../helpers/notifyStateHandler'; + +interface MainState { + chatroomparticipants: any; + loading: boolean; + count: number; + refetch: boolean; + rolesWidgets: any[]; + notify: { + showNotification: boolean; + textNotification: string; + typeNotification: string; + }; +} + +const initialState: MainState = { + chatroomparticipants: [], + loading: false, + count: 0, + refetch: false, + rolesWidgets: [], + notify: { + showNotification: false, + textNotification: '', + typeNotification: 'warn', + }, +}; + +export const fetch = createAsyncThunk( + 'chatroomparticipants/fetch', + async (data: any) => { + const { id, query } = data; + const result = await axios.get( + `chatroomparticipants${query || (id ? `/${id}` : '')}`, + ); + return id + ? result.data + : { rows: result.data.rows, count: result.data.count }; + }, +); + +export const deleteItemsByIds = createAsyncThunk( + 'chatroomparticipants/deleteByIds', + async (data: any, { rejectWithValue }) => { + try { + await axios.post('chatroomparticipants/deleteByIds', { data }); + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } + }, +); + +export const deleteItem = createAsyncThunk( + 'chatroomparticipants/deleteChatroomparticipants', + async (id: string, { rejectWithValue }) => { + try { + await axios.delete(`chatroomparticipants/${id}`); + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } + }, +); + +export const create = createAsyncThunk( + 'chatroomparticipants/createChatroomparticipants', + async (data: any, { rejectWithValue }) => { + try { + const result = await axios.post('chatroomparticipants', { data }); + return result.data; + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } + }, +); + +export const uploadCsv = createAsyncThunk( + 'chatroomparticipants/uploadCsv', + async (file: File, { rejectWithValue }) => { + try { + const data = new FormData(); + data.append('file', file); + data.append('filename', file.name); + + const result = await axios.post( + 'chatroomparticipants/bulk-import', + data, + { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }, + ); + + return result.data; + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } + }, +); + +export const update = createAsyncThunk( + 'chatroomparticipants/updateChatroomparticipants', + async (payload: any, { rejectWithValue }) => { + try { + const result = await axios.put(`chatroomparticipants/${payload.id}`, { + id: payload.id, + data: payload.data, + }); + return result.data; + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } + }, +); + +export const chatroomparticipantsSlice = createSlice({ + name: 'chatroomparticipants', + initialState, + reducers: { + setRefetch: (state, action: PayloadAction) => { + state.refetch = action.payload; + }, + }, + extraReducers: (builder) => { + builder.addCase(fetch.pending, (state) => { + state.loading = true; + resetNotify(state); + }); + builder.addCase(fetch.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }); + + builder.addCase(fetch.fulfilled, (state, action) => { + if (action.payload.rows && action.payload.count >= 0) { + state.chatroomparticipants = action.payload.rows; + state.count = action.payload.count; + } else { + state.chatroomparticipants = action.payload; + } + state.loading = false; + }); + + builder.addCase(deleteItemsByIds.pending, (state) => { + state.loading = true; + resetNotify(state); + }); + + builder.addCase(deleteItemsByIds.fulfilled, (state) => { + state.loading = false; + fulfilledNotify(state, 'Chatroomparticipants has been deleted'); + }); + + builder.addCase(deleteItemsByIds.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }); + + builder.addCase(deleteItem.pending, (state) => { + state.loading = true; + resetNotify(state); + }); + + builder.addCase(deleteItem.fulfilled, (state) => { + state.loading = false; + fulfilledNotify( + state, + `${'Chatroomparticipants'.slice(0, -1)} has been deleted`, + ); + }); + + builder.addCase(deleteItem.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }); + + builder.addCase(create.pending, (state) => { + state.loading = true; + resetNotify(state); + }); + builder.addCase(create.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }); + + builder.addCase(create.fulfilled, (state) => { + state.loading = false; + fulfilledNotify( + state, + `${'Chatroomparticipants'.slice(0, -1)} has been created`, + ); + }); + + builder.addCase(update.pending, (state) => { + state.loading = true; + resetNotify(state); + }); + builder.addCase(update.fulfilled, (state) => { + state.loading = false; + fulfilledNotify( + state, + `${'Chatroomparticipants'.slice(0, -1)} has been updated`, + ); + }); + builder.addCase(update.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }); + + builder.addCase(uploadCsv.pending, (state) => { + state.loading = true; + resetNotify(state); + }); + builder.addCase(uploadCsv.fulfilled, (state) => { + state.loading = false; + fulfilledNotify(state, 'Chatroomparticipants has been uploaded'); + }); + builder.addCase(uploadCsv.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }); + }, +}); + +// Action creators are generated for each case reducer function +export const { setRefetch } = chatroomparticipantsSlice.actions; + +export default chatroomparticipantsSlice.reducer; diff --git a/frontend/src/stores/chatrooms/chatroomsSlice.ts b/frontend/src/stores/chatrooms/chatroomsSlice.ts new file mode 100644 index 0000000..655325b --- /dev/null +++ b/frontend/src/stores/chatrooms/chatroomsSlice.ts @@ -0,0 +1,236 @@ +import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit'; +import axios from 'axios'; +import { + fulfilledNotify, + rejectNotify, + resetNotify, +} from '../../helpers/notifyStateHandler'; + +interface MainState { + chatrooms: any; + loading: boolean; + count: number; + refetch: boolean; + rolesWidgets: any[]; + notify: { + showNotification: boolean; + textNotification: string; + typeNotification: string; + }; +} + +const initialState: MainState = { + chatrooms: [], + loading: false, + count: 0, + refetch: false, + rolesWidgets: [], + notify: { + showNotification: false, + textNotification: '', + typeNotification: 'warn', + }, +}; + +export const fetch = createAsyncThunk('chatrooms/fetch', async (data: any) => { + const { id, query } = data; + const result = await axios.get(`chatrooms${query || (id ? `/${id}` : '')}`); + return id + ? result.data + : { rows: result.data.rows, count: result.data.count }; +}); + +export const deleteItemsByIds = createAsyncThunk( + 'chatrooms/deleteByIds', + async (data: any, { rejectWithValue }) => { + try { + await axios.post('chatrooms/deleteByIds', { data }); + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } + }, +); + +export const deleteItem = createAsyncThunk( + 'chatrooms/deleteChatrooms', + async (id: string, { rejectWithValue }) => { + try { + await axios.delete(`chatrooms/${id}`); + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } + }, +); + +export const create = createAsyncThunk( + 'chatrooms/createChatrooms', + async (data: any, { rejectWithValue }) => { + try { + const result = await axios.post('chatrooms', { data }); + return result.data; + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } + }, +); + +export const uploadCsv = createAsyncThunk( + 'chatrooms/uploadCsv', + async (file: File, { rejectWithValue }) => { + try { + const data = new FormData(); + data.append('file', file); + data.append('filename', file.name); + + const result = await axios.post('chatrooms/bulk-import', data, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + + return result.data; + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } + }, +); + +export const update = createAsyncThunk( + 'chatrooms/updateChatrooms', + async (payload: any, { rejectWithValue }) => { + try { + const result = await axios.put(`chatrooms/${payload.id}`, { + id: payload.id, + data: payload.data, + }); + return result.data; + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } + }, +); + +export const chatroomsSlice = createSlice({ + name: 'chatrooms', + initialState, + reducers: { + setRefetch: (state, action: PayloadAction) => { + state.refetch = action.payload; + }, + }, + extraReducers: (builder) => { + builder.addCase(fetch.pending, (state) => { + state.loading = true; + resetNotify(state); + }); + builder.addCase(fetch.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }); + + builder.addCase(fetch.fulfilled, (state, action) => { + if (action.payload.rows && action.payload.count >= 0) { + state.chatrooms = action.payload.rows; + state.count = action.payload.count; + } else { + state.chatrooms = action.payload; + } + state.loading = false; + }); + + builder.addCase(deleteItemsByIds.pending, (state) => { + state.loading = true; + resetNotify(state); + }); + + builder.addCase(deleteItemsByIds.fulfilled, (state) => { + state.loading = false; + fulfilledNotify(state, 'Chatrooms has been deleted'); + }); + + builder.addCase(deleteItemsByIds.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }); + + builder.addCase(deleteItem.pending, (state) => { + state.loading = true; + resetNotify(state); + }); + + builder.addCase(deleteItem.fulfilled, (state) => { + state.loading = false; + fulfilledNotify(state, `${'Chatrooms'.slice(0, -1)} has been deleted`); + }); + + builder.addCase(deleteItem.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }); + + builder.addCase(create.pending, (state) => { + state.loading = true; + resetNotify(state); + }); + builder.addCase(create.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }); + + builder.addCase(create.fulfilled, (state) => { + state.loading = false; + fulfilledNotify(state, `${'Chatrooms'.slice(0, -1)} has been created`); + }); + + builder.addCase(update.pending, (state) => { + state.loading = true; + resetNotify(state); + }); + builder.addCase(update.fulfilled, (state) => { + state.loading = false; + fulfilledNotify(state, `${'Chatrooms'.slice(0, -1)} has been updated`); + }); + builder.addCase(update.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }); + + builder.addCase(uploadCsv.pending, (state) => { + state.loading = true; + resetNotify(state); + }); + builder.addCase(uploadCsv.fulfilled, (state) => { + state.loading = false; + fulfilledNotify(state, 'Chatrooms has been uploaded'); + }); + builder.addCase(uploadCsv.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }); + }, +}); + +// Action creators are generated for each case reducer function +export const { setRefetch } = chatroomsSlice.actions; + +export default chatroomsSlice.reducer; diff --git a/frontend/src/stores/store.ts b/frontend/src/stores/store.ts index a20788f..d84befd 100644 --- a/frontend/src/stores/store.ts +++ b/frontend/src/stores/store.ts @@ -9,6 +9,9 @@ import departmentsSlice from './departments/departmentsSlice'; import reportsSlice from './reports/reportsSlice'; import rolesSlice from './roles/rolesSlice'; import permissionsSlice from './permissions/permissionsSlice'; +import chatroomsSlice from './chatrooms/chatroomsSlice'; +import chatmessagesSlice from './chatmessages/chatmessagesSlice'; +import chatroomparticipantsSlice from './chatroomparticipants/chatroomparticipantsSlice'; export const store = configureStore({ reducer: { @@ -22,6 +25,9 @@ export const store = configureStore({ reports: reportsSlice, roles: rolesSlice, permissions: permissionsSlice, + chatrooms: chatroomsSlice, + chatmessages: chatmessagesSlice, + chatroomparticipants: chatroomparticipantsSlice, }, }); From 38c4f6433a5f6d51583482168f826223005079d0 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Wed, 18 Jun 2025 17:08:15 +0000 Subject: [PATCH 2/2] Society Community portal --- app-shell/src/_schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-shell/src/_schema.json b/app-shell/src/_schema.json index c1141d7..8117a63 100644 --- a/app-shell/src/_schema.json +++ b/app-shell/src/_schema.json @@ -1,5 +1,5 @@ { "Initial version": "{\"iv\":\"DFwAjJtPtbc8CvjV\",\"encryptedData\":\"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\"}", "Society admin portal": "{\"iv\":\"3H2yb9Cm1+0ds1Ow\",\"encryptedData\":\"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\"}", - "Society Community portal": "{\"iv\":\"+rK8HORjiw5Wcjed\",\"encryptedData\":\"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\"}" + "Society Community portal": "{\"iv\":\"Xsbn6jNbREZRRL91\",\"encryptedData\":\"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\"}" } \ No newline at end of file