Compare commits
570 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 72ad73da8c | |||
| e4cfd0ccb1 | |||
| 1eb5c4fe12 | |||
| f385217234 | |||
| 482159fbe2 | |||
| cd1216e19f | |||
| c03e5b0f37 | |||
| b8e6001089 | |||
| 47fcf082d6 | |||
| 9fc51c511d | |||
| f3ed5e0b9a | |||
| ced651db32 | |||
| a86aa91c16 | |||
| 84b6bac819 | |||
| b17ca4fbbd | |||
| 31a204fdab | |||
| 274986186f | |||
| 4e672859c3 | |||
| 833e4f5783 | |||
| d4c7b3469e | |||
| f03df0c4c6 | |||
| ef0ea93514 | |||
| c6e38c3972 | |||
| 4b6895ccd9 | |||
| 417b27b909 | |||
| a1ec3e728b | |||
| d4ac4bface | |||
| eaa519dd69 | |||
| 4d6166a882 | |||
| 26791de867 | |||
| eb7d144b91 | |||
| 3c4ec5b11e | |||
| 102edbdb19 | |||
| 0bf2582e5c | |||
| 83e268d6d4 | |||
| e193e29499 | |||
| 703402b5b6 | |||
| c0be2a6903 | |||
| 8a9e4e75b5 | |||
| 2fecdca917 | |||
| 3ff7df6f88 | |||
| 7ce22c4801 | |||
| a675bd1d9e | |||
| c12e7fb8c3 | |||
| deff20c057 | |||
| 0128401486 | |||
| 437baf7ba5 | |||
| 895ba8e5fd | |||
| 4674a2eaaf | |||
| 00459a06b2 | |||
| 57d13504e1 | |||
| 56ba097ef4 | |||
| 2d7931f3ec | |||
| a8154e32e4 | |||
| 98287ad438 | |||
| c46e6820c7 | |||
| 34b78bf6c3 | |||
| 381f75e6a9 | |||
| 55b2d76cbb | |||
| fe3731d4cf | |||
| d09b0a34f1 | |||
| b835770cb6 | |||
| 56b3e9932e | |||
| 6c8ea1c993 | |||
| f48222bcda | |||
| 77793071ce | |||
| 7552bcf1f4 | |||
| ebbe46fc10 | |||
| 603d88c527 | |||
| 01017a9ae7 | |||
| 5a59c2b1e9 | |||
| 33ef68f27a | |||
| cbf9c27bf3 | |||
| 1da991d76d | |||
| b65868b4d5 | |||
| 2031ecc99c | |||
| 8b9c4b899f | |||
| b4d31b13af | |||
| 65bfa1d06f | |||
| 8b473c19f8 | |||
| f95e1e310d | |||
| be07381bc0 | |||
| f3d2431dfb | |||
| 8261c1c750 | |||
| 5a6a63eef6 | |||
| f1671aef2c | |||
| 4f564acd2b | |||
| fbf69f23dc | |||
| 8f8959db12 | |||
| 0ea6f1d6e3 | |||
| 21a6c26d08 | |||
| eec2dce818 | |||
| 44294ea2ce | |||
| 6555624b81 | |||
| 654015103b | |||
| 27baaa08e9 | |||
| 1cb6caafcf | |||
| 9f11f04fcf | |||
| 3bd1fb41ff | |||
| 1146d3c768 | |||
| 762f01ae71 | |||
| 64ce8ffba7 | |||
| ea7e7e3825 | |||
| ea4b2292e8 | |||
| 01660ae4a1 | |||
| 6bce625b12 | |||
|
|
2fa7b99966 | ||
| a22e47eabc | |||
| 50f8606ba8 | |||
| bbda43e4a0 | |||
| 5c85c43d5e | |||
| 17ae8f95c0 | |||
| c18851fba8 | |||
| 824fea942f | |||
| 0a309598fc | |||
|
|
c37650d942 | ||
|
|
78a8992f71 | ||
|
|
dfc3d627c8 | ||
|
|
10ddb4ffdd | ||
|
|
7f5b0cceb1 | ||
|
|
73b99b4aa0 | ||
|
|
0cf7144387 | ||
| 47f60dc309 | |||
| cae0f23522 | |||
| b029500553 | |||
|
|
da115fd824 | ||
| 03c8033540 | |||
| d34c8f34ca | |||
| 638b15fb40 | |||
|
|
7b176fe938 | ||
| f9cd2ed758 | |||
| 873c1bbb38 | |||
| db756d95c4 | |||
|
|
3f5add59e8 | ||
| 14e4f4ca72 | |||
|
|
6a082fc8f1 | ||
| 0e1eb056a4 | |||
| f45330a451 | |||
| 3111553009 | |||
| 51a03fe2c5 | |||
| 95d4c84ed8 | |||
|
|
f66eb12729 | ||
| bbecc168a0 | |||
|
|
535004bced | ||
| 6788afc3d2 | |||
|
|
3167e2c68b | ||
| 13ce9a9cc7 | |||
| 1dc87b0c06 | |||
|
|
5cf5265451 | ||
|
|
1114982553 | ||
|
|
7847bf19cc | ||
| e16e7f5f22 | |||
| f940ac4c10 | |||
| a4beb245a4 | |||
|
|
2151ac56cd | ||
| ca3e272fd9 | |||
|
|
c5a59e8de3 | ||
|
|
1b4a96c923 | ||
| 1b873f7012 | |||
| 0c9feefeb5 | |||
|
|
08337145f0 | ||
| 88bf6f8428 | |||
| a241024ced | |||
|
|
7baa780c7f | ||
|
|
2b4fcfe24c | ||
|
|
b7d6adc1cc | ||
|
|
01cce24997 | ||
|
|
f57fe9e233 | ||
| 2a2c9ac707 | |||
| ae8b58a82a | |||
| 316f6f45ed | |||
|
|
49cf88f790 | ||
|
|
d9fca5b72d | ||
| aa9f3ad444 | |||
| 92e5919c23 | |||
| 572461b179 | |||
| 42fb086d02 | |||
| 413cbae2bc | |||
| 5420fb3c9e | |||
| 41b840da96 | |||
| c00afd2256 | |||
| 7db8e966fc | |||
| f68c23b209 | |||
| 2f4477d28a | |||
| 677959e3e6 | |||
| d99e476860 | |||
| 52a3b2cd3b | |||
| dcf41675e4 | |||
| 5c2a29cc91 | |||
| 7194ba12e7 | |||
| d19d633ef0 | |||
| 4164e6ff21 | |||
| a8389d00be | |||
| ffa84949ef | |||
| d43942fb76 | |||
| 0411e4be06 | |||
|
|
2282f0da6f | ||
|
|
c5448b95c9 | ||
|
|
5dbddfdc1f | ||
|
|
2d27f95f7d | ||
|
|
deccb27213 | ||
| 7281691448 | |||
|
|
505560e7da | ||
|
|
580813792a | ||
| 8f704eda15 | |||
|
|
995088ece2 | ||
|
|
4c3c11995a | ||
|
|
582918e9e1 | ||
|
|
62434020e2 | ||
|
|
27e570156c | ||
|
|
0b2c7bda13 | ||
|
|
5c57cabb4f | ||
|
|
9c2300ad11 | ||
|
|
1f86158cef | ||
|
|
98bd619ba6 | ||
| 5bd1933ea6 | |||
| a1e930254f | |||
| 599104677d | |||
| f4b62bc8c4 | |||
|
|
6fb7ac7817 | ||
|
|
3b07eeaee2 | ||
| adc8c477c2 | |||
| f7480f5e80 | |||
|
|
52624118f6 | ||
|
|
819109893c | ||
|
|
954154e99c | ||
|
|
83507f4207 | ||
| a04cdd60d6 | |||
| e0297dff6a | |||
|
|
0f65d6c242 | ||
|
|
f13bb90aa0 | ||
|
|
9dad410c54 | ||
| 1b1236bfb2 | |||
|
|
40955b616b | ||
| af5e3a88ea | |||
| b64b426406 | |||
| a5a1757af7 | |||
| 141ca34ea0 | |||
|
|
53a11449ac | ||
|
|
cba39e8a3c | ||
|
|
688a04c3bc | ||
|
|
9fdb3087da | ||
|
|
facf1d588b | ||
| 3000a64be3 | |||
| de58b0b131 | |||
|
|
23125418c3 | ||
|
|
d9c34abcf8 | ||
|
|
296f99900a | ||
|
|
227eb9cac8 | ||
|
|
1f801d7486 | ||
|
|
5f7f40f98a | ||
|
|
d870475bc9 | ||
|
|
077780c0c3 | ||
|
|
42c9e96500 | ||
|
|
e6e8dc1b23 | ||
|
|
7a4c711e7f | ||
|
|
0e9bbcbcea | ||
|
|
30f14a5152 | ||
|
|
a6d1c089ac | ||
|
|
0df8212a65 | ||
|
|
16c832bd14 | ||
|
|
57e5b06666 | ||
|
|
54139e9e13 | ||
| 7f7b9d2ed7 | |||
| a3e1f67779 | |||
| 79b4f7a3e8 | |||
|
|
2075d0ba17 | ||
|
|
9ee3436aa3 | ||
| 299462d231 | |||
| 93fed57d4b | |||
| 933db5304f | |||
|
|
c503ddbbc2 | ||
|
|
6245fa88da | ||
|
|
e7a4e2be7a | ||
|
|
b69d1c8ce9 | ||
|
|
6ee60164ce | ||
| c11790c8b2 | |||
| 2db5532078 | |||
| acd52a5fa3 | |||
| 94c9d65ee9 | |||
| 1a25681f36 | |||
| 9b6b04796d | |||
| 1cc6381e9b | |||
| 865cfb7bc4 | |||
| bd717c388e | |||
| d412490706 | |||
| 588ddd7fec | |||
| 973ee4c1d0 | |||
| c0c984d82e | |||
| 998f476319 | |||
| 1d2f5eab2f | |||
|
|
50150b22b7 | ||
|
|
9d1ccb8e68 | ||
|
|
8fc6f4b8d1 | ||
|
|
5850adedc3 | ||
|
|
32e41397d4 | ||
|
|
9d7c577f85 | ||
|
|
49ee40b912 | ||
|
|
3bb399e6e4 | ||
|
|
5340ddf98b | ||
|
|
0ee097e82d | ||
| be3763c94f | |||
| da95606f64 | |||
| 738ad0003b | |||
| 801b7dc872 | |||
| de4fe97dc1 | |||
| ba8573f288 | |||
| ff97839cb7 | |||
| e3eaeb0b99 | |||
| ef0f46c621 | |||
| 5c8c11b03f | |||
| 014a70b8fc | |||
| ae0ccfd45b | |||
| 82c39f3993 | |||
|
|
4d91d8b331 | ||
| 5b3961064e | |||
|
|
1dd350fc8a | ||
| ef5054e8e3 | |||
|
|
c2faeb06ce | ||
|
|
9b4aad0c36 | ||
| 793053a621 | |||
| 0b25e0422a | |||
|
|
f3932b226d | ||
| 82e8e04e55 | |||
| 51d063f95c | |||
| 8b4e39416b | |||
| 1205459e53 | |||
|
|
3db45bde6c | ||
|
|
bc55580831 | ||
|
|
75341c93d8 | ||
|
|
38b830509f | ||
|
|
863692c058 | ||
|
|
30ff2fe04e | ||
| 675bf36908 | |||
| 39fdf8814a | |||
| 3ddc9fdf92 | |||
| 0cc160a02c | |||
| 70c883b01b | |||
| 0930bfcc9a | |||
| 9a764222c4 | |||
|
|
4664fe177b | ||
|
|
992da24260 | ||
|
|
2d1c1f8a46 | ||
|
|
071973e787 | ||
|
|
05a19ac7c0 | ||
|
|
e41c4d41d0 | ||
|
|
9998b31ab4 | ||
|
|
ccb0d0dc14 | ||
|
|
1e37f25a7a | ||
|
|
8d5d6f3872 | ||
| 76f22ae16a | |||
| be9f07236a | |||
| 3beb4105ea | |||
| e9d5c701f0 | |||
| 78da8716af | |||
| 3b9e1374f3 | |||
| 28f9fdcff6 | |||
| 0fdd603232 | |||
| 8fb4c82a67 | |||
| 559f3bc343 | |||
| 9e83d7929b | |||
| 930e561ce0 | |||
| 39f4b6bd8a | |||
| f2932d80c8 | |||
| 2ddf5d3249 | |||
| 656311f185 | |||
|
|
a76dfe4d9c | ||
|
|
4ad94a318f | ||
|
|
f908169bcf | ||
|
|
30ae416ba4 | ||
|
|
3e01ad07cd | ||
|
|
4fc33fedf4 | ||
|
|
2469c07fef | ||
|
|
83477904be | ||
|
|
ffef9243dd | ||
| 26f9f8c0d8 | |||
| d1e77f8ef3 | |||
| f54bc906f1 | |||
| 888591c970 | |||
| 60f344d207 | |||
| 507314b7e3 | |||
|
|
6eddcc49ec | ||
|
|
9498128ab1 | ||
|
|
492591752d | ||
|
|
31df67ef33 | ||
|
|
ce5adccdfe | ||
|
|
01f5efa991 | ||
| fa4529e123 | |||
| 2f369c0b17 | |||
|
|
713e919c10 | ||
| b423912a9a | |||
|
|
93b4ad8c50 | ||
|
|
0813bb4b44 | ||
| ada9f7fcf9 | |||
|
|
679c451c04 | ||
|
|
baa143ff00 | ||
| 89c706b893 | |||
| 7980a7b930 | |||
| 3d0e7011c1 | |||
| f0025886e9 | |||
| 0cb2ecff02 | |||
| 4d244cc1c5 | |||
| d3563e1f75 | |||
|
|
d3b9d28513 | ||
| dee517593c | |||
| e497abb09d | |||
| 98a589ec33 | |||
|
|
8261cdfc2d | ||
|
|
b4c1175ee1 | ||
| 0f0a72455b | |||
|
|
0a55014f21 | ||
|
|
094c2d4a48 | ||
|
|
9bab45f04b | ||
|
|
195c4605f0 | ||
|
|
1990022c1e | ||
|
|
ef5651d468 | ||
|
|
863a85472f | ||
|
|
37bd69c6f1 | ||
|
|
d37202edae | ||
|
|
0086bb4a5e | ||
|
|
6164a8746d | ||
|
|
867322b462 | ||
|
|
fdc6f62441 | ||
|
|
12c7384951 | ||
|
|
2b097cf4a9 | ||
|
|
c86fbedd6f | ||
|
|
ff53e4a1ec | ||
|
|
74472976d5 | ||
| e3ebb93dd2 | |||
| e1405984c4 | |||
| cf338920dd | |||
| a6485b1519 | |||
| 1187c7bcc1 | |||
| e631ae2953 | |||
| bad16acefb | |||
| 2795511a41 | |||
| 07c370983d | |||
| 2420453daa | |||
| dac5f8c4a6 | |||
| 6bde4f4e47 | |||
| 31a4356d83 | |||
| a4ed9a7afc | |||
| 682bd12873 | |||
| 969fbdfe8b | |||
| 3f6dc132f4 | |||
| f86cf72dd9 | |||
| a74e2ac0ef | |||
| 0482af966e | |||
| 2360dc2419 | |||
| 824ed7e5eb | |||
| 24b11e4152 | |||
| 3e74cdd25e | |||
|
|
0a659fad9e | ||
| 7ffea449f7 | |||
|
|
7f62331773 | ||
| 038afc415a | |||
| 28e53a4379 | |||
| b2f291f256 | |||
| 0cc4a2bedd | |||
|
|
7673504d9e | ||
| a66a779bcb | |||
| cd5dd32c73 | |||
| b5b8290131 | |||
|
|
23d765c6b5 | ||
| 419e5302f6 | |||
| 661c9b006a | |||
| 9ea5efe44e | |||
| b70afd66aa | |||
| a3d454e119 | |||
|
|
d62113c0f5 | ||
|
|
1137688d60 | ||
|
|
b14e318df2 | ||
|
|
3555b3d2a1 | ||
|
|
c99d9d21f0 | ||
|
|
6d990fbc63 | ||
|
|
e354592c47 | ||
|
|
768103cbac | ||
| 521537dc8f | |||
| 153ec836cf | |||
| 890391f982 | |||
| 1f343f4132 | |||
| 5d95613786 | |||
|
|
453cb54f19 | ||
|
|
44877cd4ad | ||
|
|
f59624f9c3 | ||
|
|
eee724d4f5 | ||
|
|
1516b92a6c | ||
|
|
933a6bff6b | ||
|
|
313ec4a57b | ||
| 072c9aadea | |||
| 1079abf0c5 | |||
| 109f967435 | |||
| 9a110d377f | |||
| 1043ac096f | |||
| 721ec1c5ce | |||
|
|
5fa9cfd5d6 | ||
| b86d03d399 | |||
|
|
63162487b5 | ||
|
|
99855a6e99 | ||
|
|
781eb48ca9 | ||
|
|
a76895434d | ||
|
|
3ee59521fd | ||
|
|
8401d9ef62 | ||
|
|
0216435221 | ||
| 6afda8e7b8 | |||
| c0f44842f7 | |||
| 3efb285e17 | |||
| a45a437d39 | |||
| 14e3b89baf | |||
| 2ac2ce339f | |||
| e1f6d8cae2 | |||
| 3b31ef4020 | |||
| 6f0eab23ee | |||
| 2a0f5e760c | |||
| 1c8d87ca54 | |||
| 84c70a1568 | |||
| 686bf1793f | |||
| 86841b035f | |||
| 4ef913dfd0 | |||
| a3851871c7 | |||
| 33c9555564 | |||
|
|
00c5198c47 | ||
| 410b68520a | |||
| 2d4e1be832 | |||
| 78aac62138 | |||
| d2a6332633 | |||
| 9c7f931df2 | |||
| 7720fb6c34 | |||
| 4a68c99c4c | |||
| b18599c280 | |||
| d5fdee10d5 | |||
| 01550bac30 | |||
| 5bd836719b | |||
| 2cacf1f852 | |||
| 896f500552 | |||
| b5efed880d | |||
| edbaabb42b | |||
| 14ca0d8809 | |||
| 909026143d | |||
|
|
67f77f4b19 | ||
| 0c173060a8 | |||
| d8622df318 | |||
| f944124972 | |||
| 5d490d7d05 | |||
| 72b423c8ad | |||
| 36ac309442 | |||
| 3b84eff536 | |||
| ab97a86977 | |||
| c37a2fdf45 | |||
| c397bfcad1 | |||
| d66508137b | |||
|
|
4dd57e3484 | ||
| 55caf434d6 | |||
| fdf438f3dd | |||
| 93e71434df | |||
| 8c6a53250a | |||
|
|
8272c49b44 | ||
|
|
38e1a29f34 | ||
|
|
5d8d37dee4 | ||
|
|
2c61e6e664 | ||
| 2fb409f0d9 | |||
|
|
dbb24085b2 | ||
|
|
75c39312fe | ||
|
|
aec9f7b9e0 | ||
| 5477c4beee | |||
| 4659aa44df | |||
| b1fe03410c | |||
| d3fdf4f823 | |||
|
|
dbdf5e54ab | ||
| b012c91b3b |
245
README.md
245
README.md
@@ -1,244 +1 @@
|
||||
# PetShop
|
||||
|
||||
A pet store management application with a Spring Boot API serving three clients: a Next.js web app, a JavaFX desktop app, and an Android app.
|
||||
|
||||
Handles product sales, pet adoption, appointment booking, real-time chat, AI assistance, payments (Stripe), email notifications (Resend), and file storage (Azure Blob).
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Layer | Technology |
|
||||
|-------|------------|
|
||||
| API | Java 25, Spring Boot 4, Spring Security (JWT), Hibernate |
|
||||
| Database | MySQL 8.0, Flyway migrations |
|
||||
| Web | Next.js 16, React 19, Tailwind CSS 4 |
|
||||
| Desktop | JavaFX, Maven |
|
||||
| Android | Kotlin, Hilt, Retrofit, CameraX |
|
||||
| Infra | Docker, Azure Container Apps |
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
main/
|
||||
backend/ Spring Boot REST API
|
||||
web/ Next.js frontend
|
||||
desktop/ JavaFX desktop client
|
||||
android/ Android mobile app
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Java 25
|
||||
- Node.js 18+
|
||||
- Docker
|
||||
- Maven
|
||||
- Android Studio (for mobile)
|
||||
|
||||
## Getting Started
|
||||
|
||||
### 1. Start the database
|
||||
|
||||
```sh
|
||||
cd backend
|
||||
docker compose -f docker-compose.dev.yml up -d
|
||||
```
|
||||
|
||||
### 2. Configure the backend
|
||||
|
||||
```sh
|
||||
cd backend
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Fill in `.env` with your keys:
|
||||
|
||||
```
|
||||
JWT_SECRET=<openssl rand -base64 32>
|
||||
STRIPE_SECRET_KEY=sk_test_...
|
||||
OPENROUTER_API_KEY=sk-or-v1-...
|
||||
RESEND_API_KEY=re_...
|
||||
RESEND_FROM=PetShop <no-reply@yourdomain.com>
|
||||
```
|
||||
|
||||
### 3. Run the backend
|
||||
|
||||
```sh
|
||||
cd backend
|
||||
mvn spring-boot:run
|
||||
```
|
||||
|
||||
The API starts at `http://localhost:8080`. Flyway runs migrations and seeds data automatically on first boot.
|
||||
|
||||
### 4. Run the web frontend
|
||||
|
||||
```sh
|
||||
cd web
|
||||
cp .env.example .env.local
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
The web app starts at `http://localhost:3000`.
|
||||
|
||||
### 5. Run the desktop client
|
||||
|
||||
```sh
|
||||
cd desktop
|
||||
cp connectionpetstore.properties.example connectionpetstore.properties
|
||||
mvn javafx:run
|
||||
```
|
||||
|
||||
### 6. Run the Android app
|
||||
|
||||
Open `android/` in Android Studio and run on an emulator or device.
|
||||
|
||||
## Switching Between Azure and Local Backend
|
||||
|
||||
Each client reads the backend URL from a config file. To point a client at the
|
||||
hosted Azure backend versus a local one, flip the commented lines.
|
||||
|
||||
### Web
|
||||
|
||||
Edit `web/.env.local`:
|
||||
|
||||
```
|
||||
# Local
|
||||
BACKEND_URL=http://localhost:8080
|
||||
#BACKEND_URL=https://petshop-backend.nicepond-c7280126.westus2.azurecontainerapps.io
|
||||
|
||||
# Azure
|
||||
#BACKEND_URL=http://localhost:8080
|
||||
BACKEND_URL=https://petshop-backend.nicepond-c7280126.westus2.azurecontainerapps.io
|
||||
```
|
||||
|
||||
Restart the dev server after changing.
|
||||
|
||||
### Desktop
|
||||
|
||||
Edit `desktop/src/main/resources/connectionpetstore.properties`:
|
||||
|
||||
```
|
||||
# Local
|
||||
api.baseUrl=http://localhost:8080
|
||||
#api.baseUrl=https://petshop-backend.nicepond-c7280126.westus2.azurecontainerapps.io
|
||||
|
||||
# Azure
|
||||
#api.baseUrl=http://localhost:8080
|
||||
api.baseUrl=https://petshop-backend.nicepond-c7280126.westus2.azurecontainerapps.io
|
||||
```
|
||||
|
||||
### Android
|
||||
|
||||
Edit `android/local.properties`:
|
||||
|
||||
```properties
|
||||
# Local (emulator — 10.0.2.2 maps to host's localhost)
|
||||
petstore.backend.emulatorUrl=http\://10.0.2.2\:8080/
|
||||
petstore.backend.deviceUrl=http\://192.168.x.x\:8080/
|
||||
|
||||
# Azure
|
||||
petstore.backend.emulatorUrl=https\://petshop-backend.nicepond-c7280126.westus2.azurecontainerapps.io/
|
||||
petstore.backend.deviceUrl=https\://petshop-backend.nicepond-c7280126.westus2.azurecontainerapps.io/
|
||||
```
|
||||
|
||||
Sync Gradle and re-run the app.
|
||||
|
||||
## API
|
||||
|
||||
A Postman collection is available at `backend/postman/`. Key endpoint groups:
|
||||
|
||||
- `/api/auth` -- registration, login, password reset
|
||||
- `/api/products` -- catalog and inventory
|
||||
- `/api/pets` -- listings and adoption
|
||||
- `/api/appointments` -- booking
|
||||
- `/api/cart`, `/api/sales`, `/api/refunds` -- transactions
|
||||
- `/api/chat` -- messaging and AI assistant
|
||||
- `/ws` -- WebSocket (STOMP) for real-time updates
|
||||
|
||||
## Docker (full stack)
|
||||
|
||||
```sh
|
||||
cd backend
|
||||
docker compose up --build -d
|
||||
```
|
||||
|
||||
Starts the API and MySQL together. The web frontend has its own Dockerfile for independent deployment.
|
||||
|
||||
## Running the Web App
|
||||
|
||||
Requires Node.js 18+.
|
||||
|
||||
```sh
|
||||
cd web
|
||||
cp .env.example .env.local
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Open `http://localhost:3000`. The app proxies API calls to the backend at `http://localhost:8080` by default.
|
||||
|
||||
To point at a different backend, edit `BACKEND_URL` and `NEXT_PUBLIC_BACKEND_URL` in `.env.local`.
|
||||
|
||||
For a production build:
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
npm run start
|
||||
```
|
||||
|
||||
## Running the Desktop App (JavaFX)
|
||||
|
||||
Requires IntelliJ IDEA and Java 25+.
|
||||
|
||||
1. Open the `desktop/` directory in IntelliJ.
|
||||
2. Copy `connectionpetstore.properties.example` to `connectionpetstore.properties` and edit it to match your database. The defaults expect the dev Docker database:
|
||||
|
||||
```
|
||||
url=jdbc:mysql://127.0.0.1:3306/Petstoredb?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
|
||||
user=petshop
|
||||
password=petshop
|
||||
```
|
||||
|
||||
3. Open **View > Tool Windows > Maven** and click **Reload All Maven Projects**.
|
||||
4. Expand **Plugins > javafx** and double-click **javafx:run**.
|
||||
|
||||
Default accounts seeded on first run:
|
||||
|
||||
| Role | Username | Password |
|
||||
|------|----------|----------|
|
||||
| Admin | `admin` | `admin123` |
|
||||
| Staff | `staff` | `staff123` |
|
||||
|
||||
## Running the Android App
|
||||
|
||||
Requires Android Studio and the Android SDK (min API 24).
|
||||
|
||||
1. Copy `local.properties.template` to `local.properties` and set `sdk.dir` to your Android SDK path.
|
||||
2. Configure the backend URLs in `local.properties`:
|
||||
|
||||
```properties
|
||||
# Emulator — 10.0.2.2 maps to the host machine's localhost
|
||||
petstore.backend.emulatorUrl=http\://10.0.2.2\:8080/
|
||||
|
||||
# Physical device — use the host machine's LAN IP
|
||||
petstore.backend.deviceUrl=http\://192.168.x.x\:8080/
|
||||
```
|
||||
|
||||
3. Open the `android/` directory in Android Studio.
|
||||
4. Sync Gradle, then run on an emulator or connected device.
|
||||
|
||||
## Running the Backend
|
||||
|
||||
Requires IntelliJ IDEA and Java 25+.
|
||||
|
||||
1. Open the `backend/` directory in IntelliJ.
|
||||
2. Copy `.env.example` to `.env` and fill in your API keys.
|
||||
3. Start the database using Docker from IntelliJ's **Services** panel, or from a terminal:
|
||||
|
||||
```sh
|
||||
cd backend
|
||||
docker compose -f docker-compose.dev.yml up -d
|
||||
```
|
||||
|
||||
4. Run the `BackendApplication` main class from IntelliJ.
|
||||
|
||||
The API starts at `http://localhost:8080`. Flyway runs migrations and seeds data automatically on first boot.
|
||||
# group-2-threaded-project-petshop
|
||||
@@ -1,9 +1,3 @@
|
||||
/*
|
||||
* Application entry point, sets up Hilt dependency injection.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
package com.example.petstoremobile;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
/*
|
||||
* Screen where the user can request a password reset email.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
package com.example.petstoremobile.activities;
|
||||
|
||||
import android.os.Bundle;
|
||||
@@ -38,9 +32,6 @@ public class ForgotPasswordActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityForgotPasswordBinding binding;
|
||||
|
||||
/**
|
||||
* Set the content view for forget password page
|
||||
*/
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
EdgeToEdge.enable(this);
|
||||
@@ -63,10 +54,6 @@ public class ForgotPasswordActivity extends AppCompatActivity {
|
||||
binding.btnBackToLogin.setOnClickListener(v -> finish());
|
||||
}
|
||||
|
||||
/**
|
||||
* A function to send a reset link to the given email address.
|
||||
* Calls the forgotPassword endpoint. To send the reset link
|
||||
* */
|
||||
private void sendResetLink(String email) {
|
||||
binding.btnSubmit.setEnabled(false);
|
||||
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
/*
|
||||
* Main home screen that shows the dashboard after the user logs in.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
package com.example.petstoremobile.activities;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@@ -29,7 +20,6 @@ import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.navigation.ui.NavigationUI;
|
||||
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.api.auth.TokenManager;
|
||||
import com.example.petstoremobile.databinding.ActivityHomeBinding;
|
||||
import com.example.petstoremobile.services.ChatNotificationService;
|
||||
|
||||
@@ -40,16 +30,6 @@ public class HomeActivity extends AppCompatActivity {
|
||||
private ActivityHomeBinding binding;
|
||||
private NavController navController;
|
||||
|
||||
private final BroadcastReceiver forceLogoutReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Intent loginIntent = new Intent(HomeActivity.this, MainActivity.class);
|
||||
loginIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
startActivity(loginIntent);
|
||||
finish();
|
||||
}
|
||||
};
|
||||
|
||||
// Launcher to ask for notification permission
|
||||
private final ActivityResultLauncher<String> requestPermissionLauncher =
|
||||
registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {
|
||||
@@ -87,22 +67,10 @@ public class HomeActivity extends AppCompatActivity {
|
||||
handleIntent(getIntent());
|
||||
}
|
||||
|
||||
IntentFilter filter = new IntentFilter(TokenManager.ACTION_FORCE_LOGOUT);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
registerReceiver(forceLogoutReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
|
||||
} else {
|
||||
registerReceiver(forceLogoutReceiver, filter);
|
||||
}
|
||||
|
||||
// Start the notification service and request for notification permission
|
||||
startNotificationService();
|
||||
requestNotificationPermission();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
unregisterReceiver(forceLogoutReceiver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles new intents received while the activity is already running (like notifications).
|
||||
@@ -110,7 +78,7 @@ public class HomeActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
setIntent(intent);
|
||||
setIntent(intent); // Set the new intent so fragments can access updated extras
|
||||
handleIntent(intent);
|
||||
}
|
||||
|
||||
@@ -135,7 +103,7 @@ public class HomeActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests for notification permission from the user if running on Android 13 and above.
|
||||
* Requests POST_NOTIFICATIONS permission from the user if running on Android 13 and above.
|
||||
*/
|
||||
private void requestNotificationPermission() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
/*
|
||||
* Entry point of the app that shows the login and registration screen.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
package com.example.petstoremobile.activities;
|
||||
|
||||
import android.content.Intent;
|
||||
@@ -104,7 +98,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform login process using the AuthViewModel and handles the authentication response.
|
||||
* Executes the login process using the AuthViewModel and handles the authentication response.
|
||||
*/
|
||||
private void performLogin(String username, String password) {
|
||||
viewModel.login(username, password).observe(this, resource -> {
|
||||
@@ -118,7 +112,6 @@ public class MainActivity extends AppCompatActivity {
|
||||
case SUCCESS:
|
||||
if (resource.data != null) {
|
||||
String role = resource.data.getRole();
|
||||
//Check if role is staff/admin or customer
|
||||
if ("CUSTOMER".equalsIgnoreCase(role)) {
|
||||
UIUtils.setViewsEnabled(true, binding.btnLogin);
|
||||
binding.tvLoginStatus.setText("Customers are not allowed to log in");
|
||||
@@ -139,7 +132,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the user's profile information to save their ID before navigating to the home screen.
|
||||
* Retrieves the logged-in user's profile information to save their ID before navigating to the home screen.
|
||||
*/
|
||||
private void fetchUserIdAndNavigate() {
|
||||
viewModel.getMe().observe(this, resource -> {
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Adapter for displaying activity log entries in a list.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
@@ -32,16 +25,10 @@ public class ActivityLogAdapter extends RecyclerView.Adapter<ActivityLogAdapter.
|
||||
|
||||
private final List<ActivityLogDTO> items;
|
||||
|
||||
/**
|
||||
* Constructor for the ActivityLogAdapter.
|
||||
*/
|
||||
public ActivityLogAdapter(List<ActivityLogDTO> items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for an activity log item and creates a new ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -50,9 +37,6 @@ public class ActivityLogAdapter extends RecyclerView.Adapter<ActivityLogAdapter.
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the data from ActivityLogDTO to the items in the ViewHolder.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
ActivityLogDTO log = items.get(position);
|
||||
@@ -87,15 +71,9 @@ public class ActivityLogAdapter extends RecyclerView.Adapter<ActivityLogAdapter.
|
||||
holder.tvTimestamp.setText(formatTimestamp(log.getLogTimestamp()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of items in the list.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() { return items.size(); }
|
||||
|
||||
/**
|
||||
* Formats the timestamp string from the API to a readable date/time format.
|
||||
*/
|
||||
private String formatTimestamp(String raw) {
|
||||
if (raw == null) return "";
|
||||
try {
|
||||
@@ -107,9 +85,6 @@ public class ActivityLogAdapter extends RecyclerView.Adapter<ActivityLogAdapter.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the Role string to be consistent
|
||||
*/
|
||||
private String formatRole(String role) {
|
||||
if (role == null) return "";
|
||||
switch (role.toUpperCase(Locale.ROOT)) {
|
||||
@@ -120,9 +95,6 @@ public class ActivityLogAdapter extends RecyclerView.Adapter<ActivityLogAdapter.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first non-null and non-blank string from the provided arguments.
|
||||
*/
|
||||
private String firstNonBlank(String... values) {
|
||||
for (String v : values) {
|
||||
if (v != null && !v.isBlank()) return v;
|
||||
@@ -130,15 +102,9 @@ public class ActivityLogAdapter extends RecyclerView.Adapter<ActivityLogAdapter.
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder class that holds references to the UI components for an activity log item.
|
||||
*/
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvActivity, tvTechnical, tvUser, tvMeta, tvTimestamp;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder by finding the views within the item layout.
|
||||
*/
|
||||
public ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
tvActivity = itemView.findViewById(R.id.tvLogActivity);
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Adapter for showing adoption records in a list.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.graphics.Color;
|
||||
@@ -30,9 +23,6 @@ public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.Adopti
|
||||
void onSelectionChanged(int count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for AdoptionAdapter.
|
||||
*/
|
||||
public AdoptionAdapter(List<AdoptionDTO> adoptionList, OnAdoptionClickListener listener) {
|
||||
this.adoptionList = adoptionList;
|
||||
this.listener = listener;
|
||||
@@ -49,17 +39,11 @@ public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.Adopti
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of IDs for the currently selected adoption items.
|
||||
*/
|
||||
@Override
|
||||
public List<String> getSelectedKeys() {
|
||||
return selectionHelper.getSelectedKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the selection state, deselecting all items.
|
||||
*/
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionHelper.clearSelection();
|
||||
@@ -74,9 +58,6 @@ public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.Adopti
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for an adoption item and creates the ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public AdoptionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -84,9 +65,6 @@ public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.Adopti
|
||||
return new AdoptionViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds adoption data to the UI components and handles click/long-click logic.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull AdoptionViewHolder holder, int position) {
|
||||
AdoptionDTO a = adoptionList.get(position);
|
||||
@@ -145,9 +123,6 @@ public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.Adopti
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of adoption items in the list.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() { return adoptionList.size(); }
|
||||
}
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Adapter for showing appointments in a list.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.graphics.Color;
|
||||
@@ -30,9 +23,6 @@ public class AppointmentAdapter extends RecyclerView.Adapter<AppointmentAdapter.
|
||||
void onSelectionChanged(int count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for AppointmentAdapter.
|
||||
*/
|
||||
public AppointmentAdapter(List<AppointmentDTO> appointmentList,
|
||||
OnAppointmentClickListener appointmentClickListener) {
|
||||
this.appointmentList = appointmentList;
|
||||
@@ -50,40 +40,25 @@ public class AppointmentAdapter extends RecyclerView.Adapter<AppointmentAdapter.
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of IDs for the currently selected appointment items.
|
||||
*/
|
||||
@Override
|
||||
public List<String> getSelectedKeys() {
|
||||
return selectionHelper.getSelectedKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the selection state, deselecting all items.
|
||||
*/
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionHelper.clearSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder class that holds references to the UI components for an appointment item.
|
||||
*/
|
||||
public static class AppointmentViewHolder extends RecyclerView.ViewHolder {
|
||||
private final ItemAppointmentBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder by finding the views within the item layout.
|
||||
*/
|
||||
public AppointmentViewHolder(@NonNull ItemAppointmentBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for an appointment item and creates the ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public AppointmentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -91,9 +66,6 @@ public class AppointmentAdapter extends RecyclerView.Adapter<AppointmentAdapter.
|
||||
return new AppointmentViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds appointment data to the UI components and handles click/long-click logic.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull AppointmentViewHolder holder, int position) {
|
||||
AppointmentDTO a = appointmentList.get(position);
|
||||
@@ -152,9 +124,6 @@ public class AppointmentAdapter extends RecyclerView.Adapter<AppointmentAdapter.
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of appointment items in the list.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return appointmentList.size();
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Custom array adapter that displays items with black text.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -19,7 +12,6 @@ import com.example.petstoremobile.R;
|
||||
import java.util.List;
|
||||
|
||||
// A class that overrides the arrayAdapter so the text color changes based on theme
|
||||
// Used to make spinners have black text on white background no matter the theme
|
||||
public class BlackTextArrayAdapter<T> extends ArrayAdapter<T> {
|
||||
public BlackTextArrayAdapter(@NonNull Context context, int resource, @NonNull T[] objects) {
|
||||
super(context, resource, objects);
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Adapter for displaying chat conversations in a list.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
@@ -27,17 +20,11 @@ public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ChatViewHolder
|
||||
void onChatClick(Chat chat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for ChatAdapter.
|
||||
*/
|
||||
public ChatAdapter(List<Chat> chatList, OnChatClickListener listener) {
|
||||
this.chatList = chatList;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for a chat item and creates the ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public ChatViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -45,9 +32,6 @@ public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ChatViewHolder
|
||||
return new ChatViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds chat data to the UI components for a specific conversation.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ChatViewHolder holder, int position) {
|
||||
Chat chat = chatList.get(position);
|
||||
@@ -56,23 +40,14 @@ public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ChatViewHolder
|
||||
holder.itemView.setOnClickListener(v -> listener.onChatClick(chat));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of chat items in the list.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return chatList.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder class that holds references to the UI components for a chat item.
|
||||
*/
|
||||
public static class ChatViewHolder extends RecyclerView.ViewHolder {
|
||||
final ItemChatBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with the chat item's view binding.
|
||||
*/
|
||||
public ChatViewHolder(@NonNull ItemChatBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Adapter for showing coupons in a list.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
@@ -37,17 +30,11 @@ public class CouponAdapter extends RecyclerView.Adapter<CouponAdapter.ViewHolder
|
||||
void onSelectionChanged(int count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for CouponAdapter.
|
||||
*/
|
||||
public CouponAdapter(List<CouponDTO> coupons, OnCouponClickListener listener) {
|
||||
this.coupons = coupons;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for a coupon item and creates the ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -55,9 +42,6 @@ public class CouponAdapter extends RecyclerView.Adapter<CouponAdapter.ViewHolder
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds coupon data to the UI components and handles interaction logic.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
CouponDTO coupon = coupons.get(position);
|
||||
@@ -111,9 +95,6 @@ public class CouponAdapter extends RecyclerView.Adapter<CouponAdapter.ViewHolder
|
||||
holder.cbSelectCoupon.setOnClickListener(v -> toggleSelection(coupon.getCouponId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the selection state of a specific coupon by its ID.
|
||||
*/
|
||||
private void toggleSelection(Long id) {
|
||||
if (selectedIds.contains(id)) {
|
||||
selectedIds.remove(id);
|
||||
@@ -124,9 +105,6 @@ public class CouponAdapter extends RecyclerView.Adapter<CouponAdapter.ViewHolder
|
||||
listener.onSelectionChanged(selectedIds.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables bulk selection mode.
|
||||
*/
|
||||
public void setSelectionMode(boolean selectionMode) {
|
||||
this.selectionMode = selectionMode;
|
||||
if (!selectionMode) selectedIds.clear();
|
||||
@@ -134,16 +112,10 @@ public class CouponAdapter extends RecyclerView.Adapter<CouponAdapter.ViewHolder
|
||||
listener.onSelectionChanged(selectedIds.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of IDs for the currently selected coupons.
|
||||
*/
|
||||
public Set<Long> getSelectedIds() {
|
||||
return selectedIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of coupons in the list.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return coupons.size();
|
||||
@@ -153,9 +125,6 @@ public class CouponAdapter extends RecyclerView.Adapter<CouponAdapter.ViewHolder
|
||||
TextView tvCouponCode, tvCouponDiscount, tvCouponMinOrder, tvCouponExpiry, tvCouponStatus;
|
||||
CheckBox cbSelectCoupon;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder by finding the views within the item layout.
|
||||
*/
|
||||
public ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
tvCouponCode = itemView.findViewById(R.id.tvCouponCode);
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Adapter for displaying customer entries in a list.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.graphics.Color;
|
||||
@@ -31,42 +24,23 @@ public class CustomerAdapter extends RecyclerView.Adapter<CustomerAdapter.Custom
|
||||
void onCustomerClick(int position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for CustomerAdapter.
|
||||
*/
|
||||
public CustomerAdapter(List<CustomerDTO> list, OnCustomerClickListener listener) {
|
||||
this.list = list;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base URL for fetching customer profile images.
|
||||
*/
|
||||
public void setBaseUrl(String baseUrl) { this.baseUrl = baseUrl; }
|
||||
|
||||
/**
|
||||
* Sets the authentication token for fetching images.
|
||||
*/
|
||||
public void setToken(String token) { this.token = token; }
|
||||
|
||||
/**
|
||||
* ViewHolder class for customer items.
|
||||
*/
|
||||
public static class CustomerViewHolder extends RecyclerView.ViewHolder {
|
||||
final ItemCustomerBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with view binding.
|
||||
*/
|
||||
public CustomerViewHolder(@NonNull ItemCustomerBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for a customer item and creates the ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public CustomerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -75,9 +49,6 @@ public class CustomerAdapter extends RecyclerView.Adapter<CustomerAdapter.Custom
|
||||
return new CustomerViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds customer data to the UI components.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull CustomerViewHolder holder, int position) {
|
||||
CustomerDTO c = list.get(position);
|
||||
@@ -104,9 +75,6 @@ public class CustomerAdapter extends RecyclerView.Adapter<CustomerAdapter.Custom
|
||||
holder.itemView.setOnClickListener(v -> listener.onCustomerClick(position));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of customers in the list.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() { return list.size(); }
|
||||
}
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Adapter for displaying employee entries in a list.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.graphics.Color;
|
||||
@@ -32,46 +25,28 @@ public class EmployeeAdapter extends RecyclerView.Adapter<EmployeeAdapter.Employ
|
||||
void onEmployeeClick(int position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for EmployeeAdapter.
|
||||
*/
|
||||
public EmployeeAdapter(List<EmployeeDTO> list, OnEmployeeClickListener listener) {
|
||||
this.list = list;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base URL for fetching employee profile images.
|
||||
*/
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication token for fetching images.
|
||||
*/
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder class for employee items.
|
||||
*/
|
||||
public static class EmployeeViewHolder extends RecyclerView.ViewHolder {
|
||||
private final ItemEmployeeBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with view binding.
|
||||
*/
|
||||
public EmployeeViewHolder(@NonNull ItemEmployeeBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for an employee item and creates the ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public EmployeeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -80,9 +55,6 @@ public class EmployeeAdapter extends RecyclerView.Adapter<EmployeeAdapter.Employ
|
||||
return new EmployeeViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds employee data to the UI components.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull EmployeeViewHolder holder, int position) {
|
||||
EmployeeDTO e = list.get(position);
|
||||
@@ -118,9 +90,6 @@ public class EmployeeAdapter extends RecyclerView.Adapter<EmployeeAdapter.Employ
|
||||
holder.itemView.setOnClickListener(v -> listener.onEmployeeClick(position));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of employees in the list.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() { return list.size(); }
|
||||
}
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Adapter for showing inventory items in a list.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.graphics.Color;
|
||||
@@ -34,9 +27,6 @@ public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.Inve
|
||||
void onSelectionChanged(int selectedCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for InventoryAdapter.
|
||||
*/
|
||||
public InventoryAdapter(List<InventoryDTO> inventoryList, OnInventoryClickListener clickListener) {
|
||||
this.inventoryList = inventoryList;
|
||||
this.clickListener = clickListener;
|
||||
@@ -53,40 +43,25 @@ public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.Inve
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of IDs for the currently selected inventory items.
|
||||
*/
|
||||
@Override
|
||||
public List<String> getSelectedKeys() {
|
||||
return selectionHelper.getSelectedKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the selection state, deselecting all items.
|
||||
*/
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionHelper.clearSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder class that holds references to the UI components for an inventory item.
|
||||
*/
|
||||
public static class InventoryViewHolder extends RecyclerView.ViewHolder {
|
||||
final ItemInventoryBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with view binding.
|
||||
*/
|
||||
public InventoryViewHolder(@NonNull ItemInventoryBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for an inventory item and creates the ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public InventoryViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -94,9 +69,6 @@ public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.Inve
|
||||
return new InventoryViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds inventory data to the UI components.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull InventoryViewHolder holder, int position) {
|
||||
InventoryDTO inv = inventoryList.get(position);
|
||||
@@ -112,6 +84,7 @@ public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.Inve
|
||||
int qty = inv.getQuantity() != null ? inv.getQuantity() : 0;
|
||||
binding.tvQuantity.setText("Stock: " + qty);
|
||||
|
||||
// Low stock = red, normal = green (like desktop reorder concept)
|
||||
if (qty <= 5) {
|
||||
binding.tvQuantity.setTextColor(Color.parseColor("#F44336"));
|
||||
} else {
|
||||
@@ -146,9 +119,6 @@ public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.Inve
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of inventory items in the list.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return inventoryList.size();
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Adapter for displaying chat messages in a conversation.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
@@ -44,64 +37,40 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
private String baseUrl;
|
||||
private OnAttachmentClickListener attachmentClickListener;
|
||||
|
||||
/**
|
||||
* Constructor for MessageAdapter.
|
||||
*/
|
||||
public MessageAdapter(List<Message> messages, Long currentUserId) {
|
||||
this.messages = messages;
|
||||
this.currentUserId = currentUserId;
|
||||
setHasStableIds(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ID for each message.
|
||||
*/
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
Message m = messages.get(position);
|
||||
return m.getId() != null ? m.getId() : position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current user's ID and refreshes the list.
|
||||
*/
|
||||
public void setCurrentUserId(Long id) {
|
||||
this.currentUserId = id;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the staff ID to identify staff messages in the UI.
|
||||
*/
|
||||
public void setStaffId(Long id) {
|
||||
this.staffId = id;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication token.
|
||||
*/
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base API URL.
|
||||
*/
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a listener for clicks on message attachments.
|
||||
*/
|
||||
public void setOnAttachmentClickListener(OnAttachmentClickListener listener) {
|
||||
this.attachmentClickListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a message is 'sent' or 'received' based on the sender's ID.
|
||||
*/
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
Message m = messages.get(position);
|
||||
@@ -111,9 +80,6 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
return TYPE_RECEIVED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the chat layout for a message.
|
||||
*/
|
||||
@NonNull @Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
LayoutInflater inf = LayoutInflater.from(parent.getContext());
|
||||
@@ -126,9 +92,6 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds message data to the appropriate ViewHolder.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||
Message m = messages.get(position);
|
||||
@@ -136,26 +99,14 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
if (holder instanceof ReceivedHolder) ((ReceivedHolder) holder).bind(m, token, baseUrl, attachmentClickListener, staffId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of messages.
|
||||
*/
|
||||
@Override public int getItemCount() { return messages.size(); }
|
||||
|
||||
/**
|
||||
* ViewHolder for messages sent by the user.
|
||||
*/
|
||||
static class SentHolder extends RecyclerView.ViewHolder {
|
||||
final ItemMessageSentBinding binding;
|
||||
/**
|
||||
* Initializes the SentHolder with view binding.
|
||||
*/
|
||||
SentHolder(ItemMessageSentBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
/**
|
||||
* Binds sent message data to the bubble UI.
|
||||
*/
|
||||
void bind(Message m, String token, String baseUrl, OnAttachmentClickListener listener) {
|
||||
binding.tvSenderName.setText("You");
|
||||
binding.tvTimestamp.setText(formatTimestamp(m.getTimestamp()));
|
||||
@@ -177,21 +128,12 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder for messages received from others.
|
||||
*/
|
||||
static class ReceivedHolder extends RecyclerView.ViewHolder {
|
||||
final ItemMessageReceivedBinding binding;
|
||||
/**
|
||||
* Initializes the ReceivedHolder with view binding.
|
||||
*/
|
||||
ReceivedHolder(ItemMessageReceivedBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
/**
|
||||
* Binds received message data to the bubble UI.
|
||||
*/
|
||||
void bind(Message m, String token, String baseUrl, OnAttachmentClickListener listener, Long staffId) {
|
||||
binding.tvSenderName.setText(resolveSenderName(m, staffId));
|
||||
binding.tvTimestamp.setText(formatTimestamp(m.getTimestamp()));
|
||||
@@ -213,9 +155,6 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the display name of the sender.
|
||||
*/
|
||||
private static String resolveSenderName(Message m, Long staffId) {
|
||||
if ("BOT".equalsIgnoreCase(m.getSenderRole())) {
|
||||
return (m.getSenderDisplayName() != null && !m.getSenderDisplayName().isEmpty())
|
||||
@@ -227,9 +166,6 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
return "Customer";
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the timestamp string into readable format.
|
||||
*/
|
||||
private static String formatTimestamp(String timestamp) {
|
||||
if (timestamp == null || timestamp.isEmpty()) return "";
|
||||
try {
|
||||
@@ -242,11 +178,8 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logic for displaying an attachment in the chat.
|
||||
*/
|
||||
private static void displayAttachment(Message m, ImageView iv, TextView tvName, String token, String baseUrl) {
|
||||
// Check if there's an attachment
|
||||
// Check if there's an attachment by looking at name or mime type
|
||||
if (m.getAttachmentName() != null || m.getAttachmentMimeType() != null) {
|
||||
// Construct the download URL using the message ID
|
||||
String url;
|
||||
@@ -254,7 +187,7 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
String cleanBase = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl;
|
||||
url = cleanBase + "/api/v1/chat/messages/" + m.getId() + "/attachment";
|
||||
} else {
|
||||
url = m.getAttachmentUrl();
|
||||
url = m.getAttachmentUrl(); // Fallback
|
||||
}
|
||||
|
||||
if (url == null) {
|
||||
@@ -275,7 +208,7 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
.build());
|
||||
}
|
||||
|
||||
// Use a signature to prevent Glide from showing cached images instead of loading new ones
|
||||
// Use a signature to prevent Glide from showing stale cached images for the same URL/ID
|
||||
String signatureKey = (m.getTimestamp() != null ? m.getTimestamp() : "") + m.getId();
|
||||
|
||||
Glide.with(iv.getContext()).clear(iv);
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Adapter for showing pets in a list.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.graphics.Color;
|
||||
@@ -38,9 +31,7 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
|
||||
void onSelectionChanged(int selectedCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for PetAdapter.
|
||||
*/
|
||||
//Constructor
|
||||
public PetAdapter(List<PetDTO> petList, OnPetClickListener petClickListener) {
|
||||
this.petList = petList;
|
||||
this.petClickListener = petClickListener;
|
||||
@@ -57,54 +48,35 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base URL for fetching pet images from the server.
|
||||
*/
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication token
|
||||
*/
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of IDs for the currently selected pet items.
|
||||
*/
|
||||
@Override
|
||||
public List<String> getSelectedKeys() {
|
||||
return selectionHelper.getSelectedKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the selection state, deselecting all items.
|
||||
*/
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionHelper.clearSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder class that holds references to the UI components for a pet item.
|
||||
*/
|
||||
// Get the controls of each row in recycler view
|
||||
public static class PetViewHolder extends RecyclerView.ViewHolder {
|
||||
private final ItemPetBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with view binding.
|
||||
*/
|
||||
public PetViewHolder(@NonNull ItemPetBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for a pet item and creates the ViewHolder.
|
||||
*/
|
||||
// Create a new row view
|
||||
@NonNull
|
||||
@Override
|
||||
public PetViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -112,9 +84,7 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
|
||||
return new PetViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds pet data to the UI components.
|
||||
*/
|
||||
//populate the row with pet data
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull PetViewHolder holder, int position) {
|
||||
PetDTO pet = petList.get(position);
|
||||
@@ -133,7 +103,7 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
|
||||
|
||||
binding.tvPetStatus.setText(pet.getPetStatus());
|
||||
|
||||
//Set the status color depending on availability
|
||||
//Set the status color depending on availability. If available, green, If Pending, yellow, otherwise red
|
||||
if (pet.getPetStatus() != null) {
|
||||
switch (pet.getPetStatus()) {
|
||||
case "Available":
|
||||
@@ -187,9 +157,6 @@ public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> i
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of pet items in the list.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return petList.size();
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Adapter for displaying products in a list.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.view.*;
|
||||
@@ -29,46 +22,28 @@ public class ProductAdapter extends RecyclerView.Adapter<ProductAdapter.ProductV
|
||||
void onProductClick(int position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for ProductAdapter.
|
||||
*/
|
||||
public ProductAdapter(List<ProductDTO> productList, OnProductClickListener listener) {
|
||||
this.productList = productList;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base URL for fetching product images.
|
||||
*/
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication token
|
||||
*/
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder class for product items.
|
||||
*/
|
||||
public static class ProductViewHolder extends RecyclerView.ViewHolder {
|
||||
final ItemProductBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with view binding.
|
||||
*/
|
||||
public ProductViewHolder(@NonNull ItemProductBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for a product item and creates the ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public ProductViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -76,9 +51,6 @@ public class ProductAdapter extends RecyclerView.Adapter<ProductAdapter.ProductV
|
||||
return new ProductViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds product data to the UI components.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ProductViewHolder holder, int position) {
|
||||
ProductDTO p = productList.get(position);
|
||||
@@ -100,9 +72,6 @@ public class ProductAdapter extends RecyclerView.Adapter<ProductAdapter.ProductV
|
||||
holder.itemView.setOnClickListener(v -> listener.onProductClick(position));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of product items in the list.
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() { return productList.size(); }
|
||||
}
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Adapter for showing product-supplier links in a list.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
@@ -32,9 +25,6 @@ public class ProductSupplierAdapter extends RecyclerView.Adapter<ProductSupplier
|
||||
void onSelectionChanged(int count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for ProductSupplierAdapter.
|
||||
*/
|
||||
public ProductSupplierAdapter(List<ProductSupplierDTO> list, OnProductSupplierClickListener listener) {
|
||||
this.list = list;
|
||||
this.listener = listener;
|
||||
@@ -51,40 +41,25 @@ public class ProductSupplierAdapter extends RecyclerView.Adapter<ProductSupplier
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of selected keys for bulk deletion.
|
||||
*/
|
||||
@Override
|
||||
public List<String> getSelectedKeys() {
|
||||
return selectionHelper.getSelectedKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all selected items and exits selection mode.
|
||||
*/
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionHelper.clearSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder for Product-Supplier relationship items.
|
||||
*/
|
||||
public static class PSViewHolder extends RecyclerView.ViewHolder {
|
||||
final ItemProductSupplierBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with view binding.
|
||||
*/
|
||||
public PSViewHolder(@NonNull ItemProductSupplierBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for a Product-Supplier item.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public PSViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -92,9 +67,6 @@ public class ProductSupplierAdapter extends RecyclerView.Adapter<ProductSupplier
|
||||
return new PSViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds product-supplier data to the UI components.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull PSViewHolder holder, int position) {
|
||||
ProductSupplierDTO ps = list.get(position);
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Adapter for displaying purchase orders in a list.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
@@ -24,32 +17,20 @@ public class PurchaseOrderAdapter extends RecyclerView.Adapter<PurchaseOrderAdap
|
||||
void onPurchaseOrderClick(int position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for PurchaseOrderAdapter.
|
||||
*/
|
||||
public PurchaseOrderAdapter(List<PurchaseOrderDTO> list, OnPurchaseOrderClickListener listener) {
|
||||
this.list = list;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder for Purchase Order items.
|
||||
*/
|
||||
public static class POViewHolder extends RecyclerView.ViewHolder {
|
||||
final ItemPurchaseOrderBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with view binding.
|
||||
*/
|
||||
public POViewHolder(@NonNull ItemPurchaseOrderBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for a Purchase Order item.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public POViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -57,9 +38,6 @@ public class PurchaseOrderAdapter extends RecyclerView.Adapter<PurchaseOrderAdap
|
||||
return new POViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds purchase order data to the UI components.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull POViewHolder holder, int position) {
|
||||
PurchaseOrderDTO po = list.get(position);
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Adapter for showing sales in a list.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
@@ -27,32 +20,20 @@ public class SaleAdapter extends RecyclerView.Adapter<SaleAdapter.SaleViewHolder
|
||||
void onSaleClick(int position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for SaleAdapter.
|
||||
*/
|
||||
public SaleAdapter(List<SaleDTO> saleList, OnSaleClickListener listener) {
|
||||
this.saleList = saleList;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder for Sale record items.
|
||||
*/
|
||||
public static class SaleViewHolder extends RecyclerView.ViewHolder {
|
||||
final ItemSaleBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with view binding.
|
||||
*/
|
||||
public SaleViewHolder(@NonNull ItemSaleBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for a Sale record item.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public SaleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -60,9 +41,6 @@ public class SaleAdapter extends RecyclerView.Adapter<SaleAdapter.SaleViewHolder
|
||||
return new SaleViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds sale transaction data to the UI components.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull SaleViewHolder holder, int position) {
|
||||
SaleDTO s = saleList.get(position);
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Adapter for displaying services in a list.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
@@ -38,9 +31,6 @@ public class ServiceAdapter extends RecyclerView.Adapter<ServiceAdapter.ServiceV
|
||||
void onSelectionChanged(int count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for ServiceAdapter.
|
||||
*/
|
||||
public ServiceAdapter(List<ServiceDTO> serviceList, OnServiceClickListener clickListener) {
|
||||
this.serviceList = serviceList;
|
||||
this.clickListener = clickListener;
|
||||
@@ -57,40 +47,29 @@ public class ServiceAdapter extends RecyclerView.Adapter<ServiceAdapter.ServiceV
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of selected keys for bulk deletion.
|
||||
*/
|
||||
@Override
|
||||
public List<String> getSelectedKeys() {
|
||||
return selectionHelper.getSelectedKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all selected items and exits selection mode.
|
||||
*/
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionHelper.clearSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder for Service items.
|
||||
* ViewHolder class for service items.
|
||||
*/
|
||||
public static class ServiceViewHolder extends RecyclerView.ViewHolder {
|
||||
final ItemServiceBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with view binding.
|
||||
*/
|
||||
public ServiceViewHolder(@NonNull ItemServiceBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for a Service item.
|
||||
*/
|
||||
// Create a new row view
|
||||
@NonNull
|
||||
@Override
|
||||
public ServiceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -98,9 +77,7 @@ public class ServiceAdapter extends RecyclerView.Adapter<ServiceAdapter.ServiceV
|
||||
return new ServiceViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds service data to the UI components.
|
||||
*/
|
||||
//populate the row with service data
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ServiceViewHolder holder, int position) {
|
||||
ServiceDTO service = serviceList.get(position);
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Adapter for showing suppliers in a list.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
@@ -33,9 +26,7 @@ public class SupplierAdapter extends RecyclerView.Adapter<SupplierAdapter.Suppli
|
||||
void onSelectionChanged(int count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for SupplierAdapter.
|
||||
*/
|
||||
//Constructor
|
||||
public SupplierAdapter(List<SupplierDTO> supplierList, OnSupplierClickListener supplierClickListener) {
|
||||
this.supplierList = supplierList;
|
||||
this.supplierClickListener = supplierClickListener;
|
||||
@@ -52,40 +43,27 @@ public class SupplierAdapter extends RecyclerView.Adapter<SupplierAdapter.Suppli
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of selected keys for bulk deletion.
|
||||
*/
|
||||
@Override
|
||||
public List<String> getSelectedKeys() {
|
||||
return selectionHelper.getSelectedKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all selected items and exits selection mode.
|
||||
*/
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionHelper.clearSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder for Supplier items.
|
||||
*/
|
||||
// Get the controls of each row in recycler view
|
||||
public static class SupplierViewHolder extends RecyclerView.ViewHolder {
|
||||
final ItemSupplierBinding binding;
|
||||
|
||||
/**
|
||||
* Initializes the ViewHolder with view binding.
|
||||
*/
|
||||
public SupplierViewHolder(@NonNull ItemSupplierBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for a Supplier item.
|
||||
*/
|
||||
// Create a new row view
|
||||
@NonNull
|
||||
@Override
|
||||
public SupplierViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -93,9 +71,7 @@ public class SupplierAdapter extends RecyclerView.Adapter<SupplierAdapter.Suppli
|
||||
return new SupplierViewHolder(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds supplier data to the UI components.
|
||||
*/
|
||||
//populate the row with supplier data
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull SupplierViewHolder holder, int position) {
|
||||
SupplierDTO supplier = supplierList.get(position);
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Custom array adapter that displays items with white text.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -19,8 +12,9 @@ import androidx.core.content.ContextCompat;
|
||||
import com.example.petstoremobile.R;
|
||||
import java.util.List;
|
||||
|
||||
// A class that overrides the arrayAdapter so the text color changes based on theme
|
||||
// Used to make spinners have white text on dark background no matter the theme
|
||||
/**
|
||||
* A class that overrides the arrayAdapter so the text color is white and background is transparent.
|
||||
*/
|
||||
public class WhiteTextArrayAdapter<T> extends ArrayAdapter<T> {
|
||||
public WhiteTextArrayAdapter(@NonNull Context context, int resource, @NonNull T[] objects) {
|
||||
super(context, resource, objects);
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for activity log endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.ActivityLogDTO;
|
||||
@@ -15,10 +8,8 @@ import retrofit2.Call;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls to get activity logs
|
||||
public interface ActivityLogApi {
|
||||
|
||||
// Get activity logs with filters
|
||||
@GET("api/v1/activity-logs")
|
||||
Call<List<ActivityLogDTO>> getActivityLogs(
|
||||
@Query("limit") int limit,
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for adoption endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.AdoptionDTO;
|
||||
@@ -21,10 +14,8 @@ import retrofit2.http.PUT;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls for adoptions
|
||||
public interface AdoptionApi {
|
||||
|
||||
// Get all adoptions with filters
|
||||
@GET("api/v1/adoptions")
|
||||
Call<PageResponse<AdoptionDTO>> getAllAdoptions(
|
||||
@Query("page") int page,
|
||||
@@ -36,23 +27,18 @@ public interface AdoptionApi {
|
||||
@Query("employeeId") Long employeeId,
|
||||
@Query("sort") String sort);
|
||||
|
||||
// Get adoption by id
|
||||
@GET("api/v1/adoptions/{id}")
|
||||
Call<AdoptionDTO> getAdoptionById(@Path("id") Long id);
|
||||
|
||||
// Create adoption
|
||||
@POST("api/v1/adoptions")
|
||||
Call<AdoptionDTO> createAdoption(@Body AdoptionDTO adoption);
|
||||
|
||||
// Update adoption
|
||||
@PUT("api/v1/adoptions/{id}")
|
||||
Call<AdoptionDTO> updateAdoption(@Path("id") Long id, @Body AdoptionDTO adoption);
|
||||
|
||||
// Delete adoption
|
||||
@DELETE("api/v1/adoptions/{id}")
|
||||
Call<Void> deleteAdoption(@Path("id") Long id);
|
||||
|
||||
// Bulk delete adoptions
|
||||
@HTTP(method = "DELETE", path = "api/v1/adoptions", hasBody = true)
|
||||
Call<Void> bulkDeleteAdoptions(@Body BulkDeleteRequest request);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for appointment endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.AppointmentDTO;
|
||||
@@ -21,10 +14,8 @@ import retrofit2.http.PUT;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls for appointments
|
||||
public interface AppointmentApi {
|
||||
|
||||
// Get all appointments with filters
|
||||
@GET("api/v1/appointments")
|
||||
Call<PageResponse<AppointmentDTO>> getAllAppointments(
|
||||
@Query("page") int page,
|
||||
@@ -36,23 +27,18 @@ public interface AppointmentApi {
|
||||
@Query("employeeId") Long employeeId,
|
||||
@Query("sort") String sort);
|
||||
|
||||
// Get appointment by id
|
||||
@GET("api/v1/appointments/{id}")
|
||||
Call<AppointmentDTO> getAppointmentById(@Path("id") Long id);
|
||||
|
||||
// Create appointment
|
||||
@POST("api/v1/appointments")
|
||||
Call<AppointmentDTO> createAppointment(@Body AppointmentDTO appointment);
|
||||
|
||||
// Update appointment
|
||||
@PUT("api/v1/appointments/{id}")
|
||||
Call<AppointmentDTO> updateAppointment(@Path("id") Long id, @Body AppointmentDTO appointment);
|
||||
|
||||
// Delete appointment
|
||||
@DELETE("api/v1/appointments/{id}")
|
||||
Call<Void> deleteAppointment(@Path("id") Long id);
|
||||
|
||||
// Bulk delete appointments
|
||||
@HTTP(method = "DELETE", path = "api/v1/appointments", hasBody = true)
|
||||
Call<Void> bulkDeleteAppointments(@Body BulkDeleteRequest request);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for category endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.CategoryDTO;
|
||||
@@ -13,10 +6,8 @@ import retrofit2.Call;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls for categories
|
||||
public interface CategoryApi {
|
||||
|
||||
// Get all categories with pagination
|
||||
@GET("api/v1/categories")
|
||||
Call<PageResponse<CategoryDTO>> getAllCategories(
|
||||
@Query("page") int page,
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for chat and conversation endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.ConversationDTO;
|
||||
@@ -19,18 +12,15 @@ import retrofit2.http.GET;
|
||||
import retrofit2.http.PUT;
|
||||
import retrofit2.http.Path;
|
||||
|
||||
// api calls for chat conversations
|
||||
//api calls to get conversations
|
||||
public interface ChatApi {
|
||||
|
||||
// Get all conversations
|
||||
@GET("api/v1/chat/conversations")
|
||||
Call<List<ConversationDTO>> getAllConversations();
|
||||
|
||||
// Get conversation by id
|
||||
@GET("api/v1/chat/conversations/{conversationId}")
|
||||
Call<ConversationDTO> getConversationById(@Path("conversationId") Long conversationId);
|
||||
|
||||
// Update conversation status
|
||||
@PUT("api/v1/chat/conversations/{conversationId}")
|
||||
Call<ConversationDTO> updateConversationStatus(@Path("conversationId") Long conversationId, @Body UpdateConversationStatusRequest request);
|
||||
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for coupon endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.CouponDTO;
|
||||
@@ -13,12 +6,9 @@ import com.example.petstoremobile.dtos.PageResponse;
|
||||
import java.util.List;
|
||||
|
||||
import retrofit2.Call;
|
||||
import com.example.petstoremobile.dtos.BulkDeleteRequest;
|
||||
|
||||
import retrofit2.http.Body;
|
||||
import retrofit2.http.DELETE;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.HTTP;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.PUT;
|
||||
import retrofit2.http.Path;
|
||||
@@ -49,6 +39,6 @@ public interface CouponApi {
|
||||
@DELETE("api/v1/coupons/{id}")
|
||||
Call<Void> deleteCoupon(@Path("id") Long id);
|
||||
|
||||
@HTTP(method = "DELETE", path = "api/v1/coupons", hasBody = true)
|
||||
Call<Void> bulkDeleteCoupons(@Body BulkDeleteRequest request);
|
||||
@DELETE("api/v1/coupons")
|
||||
Call<Void> bulkDeleteCoupons(@Query("ids") List<Long> ids);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for customer endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.CustomerDTO;
|
||||
@@ -22,30 +15,23 @@ import retrofit2.http.PUT;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls for customers
|
||||
public interface CustomerApi {
|
||||
|
||||
// Get all customers with pagination
|
||||
@GET("api/v1/customers")
|
||||
Call<PageResponse<CustomerDTO>> getAllCustomers(@Query("page") int page, @Query("size") int size);
|
||||
|
||||
// Get customer by id
|
||||
@GET("api/v1/customers/{customerId}")
|
||||
Call<CustomerDTO> getCustomerById(@Path("customerId") Long customerId);
|
||||
|
||||
// Update customer
|
||||
@PUT("api/v1/customers/{customerId}")
|
||||
Call<CustomerDTO> updateCustomer(@Path("customerId") Long customerId, @Body CustomerDTO customer);
|
||||
|
||||
// Delete customer
|
||||
@DELETE("api/v1/customers/{customerId}")
|
||||
Call<Void> deleteCustomer(@Path("customerId") Long customerId);
|
||||
|
||||
// Register customer
|
||||
@POST("api/v1/auth/register")
|
||||
Call<CustomerDTO> registerCustomer(@Body CustomerDTO customer);
|
||||
|
||||
// Get customer dropdowns
|
||||
@GET("api/v1/dropdowns/customers")
|
||||
Call<List<DropdownDTO>> getCustomerDropdowns();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for employee endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.EmployeeDTO;
|
||||
@@ -12,28 +5,28 @@ import com.example.petstoremobile.dtos.PageResponse;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.*;
|
||||
|
||||
// api calls for employees
|
||||
public interface EmployeeApi {
|
||||
|
||||
// Get all employees with pagination
|
||||
@GET("api/v1/employees")
|
||||
Call<PageResponse<EmployeeDTO>> getAllEmployees(
|
||||
@Query("page") int page,
|
||||
@Query("size") int size);
|
||||
|
||||
// Get employee by id
|
||||
@GET("api/v1/employees/{id}")
|
||||
Call<EmployeeDTO> getEmployeeById(@Path("id") Long id);
|
||||
|
||||
// Create employee
|
||||
@POST("api/v1/employees")
|
||||
Call<EmployeeDTO> createEmployee(@Body EmployeeDTO employee);
|
||||
|
||||
// Update employee
|
||||
@PUT("api/v1/employees/{id}")
|
||||
Call<EmployeeDTO> updateEmployee(@Path("id") Long id, @Body EmployeeDTO employee);
|
||||
|
||||
// Delete employee
|
||||
@DELETE("api/v1/employees/{id}")
|
||||
Call<Void> deleteEmployee(@Path("id") Long id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for inventory endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.BulkDeleteRequest;
|
||||
@@ -21,10 +14,8 @@ import retrofit2.http.PUT;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls for inventory
|
||||
public interface InventoryApi {
|
||||
|
||||
// Get all inventory with filters
|
||||
@GET("api/v1/inventory")
|
||||
Call<PageResponse<InventoryDTO>> getAllInventory(
|
||||
@Query("page") int page,
|
||||
@@ -33,23 +24,23 @@ public interface InventoryApi {
|
||||
@Query("storeId") Long storeId,
|
||||
@Query("sort") String sort);
|
||||
|
||||
// Get inventory by id
|
||||
// GET /api/v1/inventory/{id}
|
||||
@GET("api/v1/inventory/{id}")
|
||||
Call<InventoryDTO> getInventoryById(@Path("id") Long id);
|
||||
|
||||
// Create inventory
|
||||
// POST /api/v1/inventory
|
||||
@POST("api/v1/inventory")
|
||||
Call<InventoryDTO> createInventory(@Body InventoryDTO request);
|
||||
|
||||
// Update inventory
|
||||
// PUT /api/v1/inventory/{id}
|
||||
@PUT("api/v1/inventory/{id}")
|
||||
Call<InventoryDTO> updateInventory(@Path("id") Long id, @Body InventoryDTO request);
|
||||
|
||||
// Delete inventory
|
||||
// DELETE /api/v1/inventory/{id}
|
||||
@DELETE("api/v1/inventory/{id}")
|
||||
Call<Void> deleteInventory(@Path("id") Long id);
|
||||
|
||||
// Bulk delete inventory
|
||||
// DELETE /api/v1/inventory (bulk delete)
|
||||
@HTTP(method = "DELETE", path = "api/v1/inventory", hasBody = true)
|
||||
Call<Void> bulkDeleteInventory(@Body BulkDeleteRequest request);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for message endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.MessageDTO;
|
||||
@@ -22,18 +15,15 @@ import retrofit2.http.Part;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Streaming;
|
||||
|
||||
// api calls for messages
|
||||
//api calls to get and send messages
|
||||
public interface MessageApi {
|
||||
|
||||
// Get messages for a conversation
|
||||
@GET("api/v1/chat/conversations/{id}/messages")
|
||||
Call<List<MessageDTO>> getMessages(@Path("id") Long conversationId);
|
||||
|
||||
// Send a message
|
||||
@POST("api/v1/chat/conversations/{id}/messages")
|
||||
Call<MessageDTO> sendMessage(@Path("id") Long conversationId, @Body SendMessageRequest request);
|
||||
|
||||
// Send a message with attachment
|
||||
@Multipart
|
||||
@POST("api/v1/chat/conversations/{id}/attachments")
|
||||
Call<MessageDTO> sendMessageWithAttachment(
|
||||
@@ -42,7 +32,6 @@ public interface MessageApi {
|
||||
@Part MultipartBody.Part file
|
||||
);
|
||||
|
||||
// Download attachment
|
||||
@GET("api/v1/chat/messages/{id}/attachment")
|
||||
@Streaming
|
||||
Call<ResponseBody> downloadAttachment(@Path("id") Long messageId);
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for pet endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.BulkDeleteRequest;
|
||||
@@ -27,7 +20,7 @@ import retrofit2.http.Part;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls to CRUD pets
|
||||
//api calls to CRUD pets
|
||||
public interface PetApi {
|
||||
// endpoint for downloading the pet's image file
|
||||
String PET_IMAGE_PATH = "api/v1/pets/%d/image";
|
||||
@@ -45,23 +38,18 @@ public interface PetApi {
|
||||
@Query("sort") String sort
|
||||
);
|
||||
|
||||
// Get pets by customer id
|
||||
@GET("api/v1/dropdowns/customers/{customerId}/pets")
|
||||
Call<List<DropdownDTO>> getCustomerPets(@Path("customerId") Long customerId);
|
||||
|
||||
// Get adoption pets
|
||||
@GET("api/v1/dropdowns/adoption-pets")
|
||||
Call<List<DropdownDTO>> getAdoptionPets(@Query("storeId") Long storeId);
|
||||
|
||||
// Get pet dropdowns
|
||||
@GET("api/v1/dropdowns/pets")
|
||||
Call<List<DropdownDTO>> getPetDropdowns();
|
||||
|
||||
// Get pet species dropdowns
|
||||
@GET("api/v1/dropdowns/pet-species")
|
||||
Call<List<DropdownDTO>> getPetSpeciesDropdowns();
|
||||
|
||||
// Get pet breeds dropdowns
|
||||
@GET("api/v1/dropdowns/pet-breeds")
|
||||
Call<List<DropdownDTO>> getPetBreedsDropdowns(@Query("species") String species);
|
||||
|
||||
@@ -93,4 +81,5 @@ public interface PetApi {
|
||||
// Delete pet image
|
||||
@DELETE("api/v1/pets/{id}/image")
|
||||
Call<Void> deletePetImage(@Path("id") Long id);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for product endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.DropdownDTO;
|
||||
@@ -16,12 +9,9 @@ import retrofit2.http.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
// api calls for products
|
||||
public interface ProductApi {
|
||||
// endpoint for downloading the product's image file
|
||||
String PRODUCT_IMAGE_PATH = "api/v1/products/%d/image";
|
||||
|
||||
// Get all products with filters
|
||||
@GET("api/v1/products")
|
||||
Call<PageResponse<ProductDTO>> getAllProducts(
|
||||
@Query("q") String query,
|
||||
@@ -30,36 +20,28 @@ public interface ProductApi {
|
||||
@Query("size") int size,
|
||||
@Query("sort") String sort);
|
||||
|
||||
// Get product by id
|
||||
@GET("api/v1/products/{id}")
|
||||
Call<ProductDTO> getProductById(@Path("id") Long id);
|
||||
|
||||
// Create product
|
||||
@POST("api/v1/products")
|
||||
Call<ProductDTO> createProduct(@Body ProductDTO product);
|
||||
|
||||
// Update product
|
||||
@PUT("api/v1/products/{id}")
|
||||
Call<ProductDTO> updateProduct(@Path("id") Long id, @Body ProductDTO product);
|
||||
|
||||
// Delete product
|
||||
@DELETE("api/v1/products/{id}")
|
||||
Call<Void> deleteProduct(@Path("id") Long id);
|
||||
|
||||
// Upload product image
|
||||
@Multipart
|
||||
@POST("api/v1/products/{id}/image")
|
||||
Call<Void> uploadProductImage(@Path("id") Long id, @Part MultipartBody.Part image);
|
||||
|
||||
// Delete product image
|
||||
@DELETE("api/v1/products/{id}/image")
|
||||
Call<Void> deleteProductImage(@Path("id") Long id);
|
||||
|
||||
// Get product dropdowns
|
||||
@GET("api/v1/dropdowns/products")
|
||||
Call<List<DropdownDTO>> getProductDropdowns();
|
||||
|
||||
// Get category dropdowns
|
||||
@GET("api/v1/dropdowns/categories")
|
||||
Call<List<DropdownDTO>> getCategoryDropdowns();
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for product-supplier endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.BulkDeleteRequest;
|
||||
@@ -13,10 +6,8 @@ import com.example.petstoremobile.dtos.ProductSupplierDTO;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.*;
|
||||
|
||||
// api calls for product-supplier relationships
|
||||
public interface ProductSupplierApi {
|
||||
|
||||
// Get all product-suppliers with filters
|
||||
@GET("api/v1/product-suppliers")
|
||||
Call<PageResponse<ProductSupplierDTO>> getAllProductSuppliers(
|
||||
@Query("page") int page,
|
||||
@@ -26,30 +17,24 @@ public interface ProductSupplierApi {
|
||||
@Query("supplierId") Long supplierId,
|
||||
@Query("sort") String sort);
|
||||
|
||||
// Get product-supplier by composite id
|
||||
@GET("api/v1/product-suppliers/{productId}/{supplierId}")
|
||||
Call<ProductSupplierDTO> getProductSupplierById(
|
||||
@Path("productId") Long productId,
|
||||
@Path("supplierId") Long supplierId);
|
||||
|
||||
// Create product-supplier
|
||||
@POST("api/v1/product-suppliers")
|
||||
Call<ProductSupplierDTO> createProductSupplier(@Body ProductSupplierDTO dto);
|
||||
|
||||
// Update product-supplier
|
||||
@PUT("api/v1/product-suppliers/{productId}/{supplierId}")
|
||||
Call<ProductSupplierDTO> updateProductSupplier(
|
||||
@Path("productId") Long productId,
|
||||
@Path("supplierId") Long supplierId,
|
||||
@Body ProductSupplierDTO dto);
|
||||
|
||||
// Delete product-supplier
|
||||
@DELETE("api/v1/product-suppliers/{productId}/{supplierId}")
|
||||
Call<Void> deleteProductSupplier(
|
||||
@Path("productId") Long productId,
|
||||
@Path("supplierId") Long supplierId);
|
||||
|
||||
// Bulk delete product-suppliers
|
||||
@HTTP(method = "DELETE", path = "api/v1/product-suppliers", hasBody = true)
|
||||
Call<Void> bulkDeleteProductSuppliers(@Body BulkDeleteRequest request);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for purchase order endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
@@ -14,10 +7,8 @@ import retrofit2.http.GET;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls for purchase orders
|
||||
public interface PurchaseOrderApi {
|
||||
|
||||
// Get all purchase orders with filters
|
||||
@GET("api/v1/purchase-orders")
|
||||
Call<PageResponse<PurchaseOrderDTO>> getAllPurchaseOrders(
|
||||
@Query("page") int page,
|
||||
@@ -26,7 +17,6 @@ public interface PurchaseOrderApi {
|
||||
@Query("storeId") Long storeId,
|
||||
@Query("sort") String sort);
|
||||
|
||||
// Get purchase order by id
|
||||
@GET("api/v1/purchase-orders/{id}")
|
||||
Call<PurchaseOrderDTO> getPurchaseOrderById(@Path("id") Long id);
|
||||
}
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for refund endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.RefundDTO;
|
||||
@@ -13,26 +6,20 @@ import retrofit2.http.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
// api calls for refunds
|
||||
public interface RefundApi {
|
||||
|
||||
// Get all refunds
|
||||
@GET("api/v1/refunds")
|
||||
Call<List<RefundDTO>> getAllRefunds();
|
||||
|
||||
// Get refund by id
|
||||
@GET("api/v1/refunds/{id}")
|
||||
Call<RefundDTO> getRefundById(@Path("id") Long id);
|
||||
|
||||
// Create refund
|
||||
@POST("api/v1/refunds")
|
||||
Call<RefundDTO> createRefund(@Body RefundDTO refund);
|
||||
|
||||
// Update refund
|
||||
@PUT("api/v1/refunds/{id}")
|
||||
Call<RefundDTO> updateRefund(@Path("id") Long id, @Body RefundDTO refund);
|
||||
|
||||
// Delete refund
|
||||
@DELETE("api/v1/refunds/{id}")
|
||||
Call<Void> deleteRefund(@Path("id") Long id);
|
||||
}
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for sale endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
@@ -17,10 +10,8 @@ import retrofit2.http.POST;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls for sales
|
||||
public interface SaleApi {
|
||||
|
||||
// Get all sales with filters
|
||||
@GET("api/v1/sales")
|
||||
Call<PageResponse<SaleDTO>> getAllSales(
|
||||
@Query("page") int page,
|
||||
@@ -32,11 +23,9 @@ public interface SaleApi {
|
||||
@Query("customerId") Long customerId,
|
||||
@Query("sort") String sort);
|
||||
|
||||
// Get sale by id
|
||||
@GET("api/v1/sales/{id}")
|
||||
Call<SaleDTO> getSaleById(@Path("id") Long id);
|
||||
|
||||
// Create sale
|
||||
@POST("api/v1/sales")
|
||||
Call<SaleDTO> createSale(@Body SaleDTO sale);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for service endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.BulkDeleteRequest;
|
||||
@@ -21,7 +14,7 @@ import retrofit2.http.PUT;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls to CRUD services
|
||||
//api calls to CRUD services
|
||||
public interface ServiceApi {
|
||||
// Get all services
|
||||
@GET("api/v1/services")
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for store endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.DropdownDTO;
|
||||
@@ -18,20 +11,16 @@ import retrofit2.http.GET;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls for stores
|
||||
public interface StoreApi {
|
||||
|
||||
// Get all stores with pagination
|
||||
@GET("api/v1/stores")
|
||||
Call<PageResponse<StoreDTO>> getAllStores(
|
||||
@Query("page") int page,
|
||||
@Query("size") int size);
|
||||
|
||||
// Get store dropdowns
|
||||
@GET("api/v1/dropdowns/stores")
|
||||
Call<List<DropdownDTO>> getStoreDropdowns();
|
||||
|
||||
// Get employees of a specific store
|
||||
@GET("api/v1/dropdowns/stores/{storeId}/employees")
|
||||
Call<List<DropdownDTO>> getStoreEmployees(@Path("storeId") Long storeId);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for supplier endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.BulkDeleteRequest;
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for user endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
@@ -14,12 +7,9 @@ import retrofit2.Call;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
// api calls for users
|
||||
public interface UserApi {
|
||||
// endpoint for downloading the user's avatar file
|
||||
String AVATAR_PATH = "api/v1/users/%d/avatar/file";
|
||||
|
||||
// Get all users with filters
|
||||
@GET("api/v1/users")
|
||||
Call<PageResponse<UserDTO>> getUsers(@Query("role") String role, @Query("page") int page, @Query("size") int size);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Retrofit interface for login and registration endpoints.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api.auth;
|
||||
|
||||
import com.example.petstoremobile.dtos.AuthDTO;
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Interceptor that attaches the auth token to outgoing requests.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api.auth;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -30,26 +23,19 @@ public class AuthInterceptor implements Interceptor {
|
||||
String token = tokenManager.getToken();
|
||||
String url = chain.request().url().toString();
|
||||
|
||||
boolean isAuthEndpoint = url.contains("auth/login") || url.contains("auth/register");
|
||||
|
||||
if (isAuthEndpoint) {
|
||||
if (url.contains("auth/login") || url.contains("auth/register")) {
|
||||
return chain.proceed(chain.request());
|
||||
}
|
||||
|
||||
Response response;
|
||||
//If we have a token then add it to the request
|
||||
if (token != null) {
|
||||
Request request = chain.request().newBuilder()
|
||||
.addHeader("Authorization", "Bearer " + token)
|
||||
.build();
|
||||
response = chain.proceed(request);
|
||||
} else {
|
||||
response = chain.proceed(chain.request());
|
||||
return chain.proceed(request);
|
||||
}
|
||||
|
||||
if (response.code() == 401) {
|
||||
tokenManager.forceLogout();
|
||||
}
|
||||
|
||||
return response;
|
||||
//If no token then just pass the request
|
||||
return chain.proceed(chain.request());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
/*
|
||||
* Handles saving and retrieving the authentication token.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.api.auth;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -16,11 +8,8 @@ import javax.inject.Singleton;
|
||||
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext;
|
||||
|
||||
//Used to save and retrieve login data
|
||||
@Singleton
|
||||
public class TokenManager {
|
||||
public static final String ACTION_FORCE_LOGOUT = "com.example.petstoremobile.ACTION_FORCE_LOGOUT";
|
||||
|
||||
private static final String TOKEN_KEY = "token";
|
||||
private static final String USERNAME_KEY = "username";
|
||||
private static final String ROLE_KEY = "role";
|
||||
@@ -28,22 +17,13 @@ public class TokenManager {
|
||||
private static final String USER_ID_KEY = "user_id";
|
||||
private static final String PRIMARY_STORE_ID_KEY = "primary_store_id";
|
||||
|
||||
private final Context context;
|
||||
private SharedPreferences prefs;
|
||||
|
||||
@Inject
|
||||
public TokenManager(@ApplicationContext Context context) {
|
||||
this.context = context;
|
||||
prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public void forceLogout() {
|
||||
clearLoginData();
|
||||
Intent intent = new Intent(ACTION_FORCE_LOGOUT);
|
||||
intent.setPackage(context.getPackageName());
|
||||
context.sendBroadcast(intent);
|
||||
}
|
||||
|
||||
//save login data after login
|
||||
public void saveLoginData(String token, String username, String role) {
|
||||
prefs.edit()
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Sets up the Retrofit client and dependency injection for network calls.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.di;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
/*
|
||||
* Data transfer object for activity log entries.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for activity logs.
|
||||
*/
|
||||
public class ActivityLogDTO {
|
||||
private Long logId;
|
||||
private String activity;
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
/*
|
||||
* Data transfer object for adoption records.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for pet adoptions.
|
||||
*/
|
||||
public class AdoptionDTO {
|
||||
|
||||
private Long adoptionId;
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
/*
|
||||
* Data transfer object for appointments.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for appointments.
|
||||
*/
|
||||
public class AppointmentDTO {
|
||||
|
||||
private Long appointmentId;
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
/*
|
||||
* Data transfer object for authentication requests and responses.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for authentication credentials.
|
||||
*/
|
||||
//Used to send login data to the backend
|
||||
public class AuthDTO {
|
||||
public static class LoginRequest {
|
||||
private String username;
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
/*
|
||||
* Response object returned after uploading an avatar image.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Response containing the URL of a newly uploaded avatar.
|
||||
*/
|
||||
public class AvatarUploadResponse {
|
||||
private String avatarUrl;
|
||||
private String message;
|
||||
|
||||
@@ -1,32 +1,22 @@
|
||||
/*
|
||||
* Request object for deleting multiple items at once.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Request body for deleting multiple records at once.
|
||||
*/
|
||||
public class BulkDeleteRequest {
|
||||
private List<Long> ids;
|
||||
private List<String> ids;
|
||||
|
||||
public BulkDeleteRequest() {
|
||||
}
|
||||
|
||||
public BulkDeleteRequest(List<Long> ids) {
|
||||
public BulkDeleteRequest(List<String> ids) {
|
||||
this.ids = ids;
|
||||
}
|
||||
|
||||
public List<Long> getIds() {
|
||||
public List<String> getIds() {
|
||||
return ids;
|
||||
}
|
||||
|
||||
public void setIds(List<Long> ids) {
|
||||
public void setIds(List<String> ids) {
|
||||
this.ids = ids;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
/*
|
||||
* Data transfer object for categories.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for product categories.
|
||||
*/
|
||||
public class CategoryDTO {
|
||||
private Long categoryId;
|
||||
private String categoryName;
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
/*
|
||||
* Data transfer object for chat conversations.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for chat conversations.
|
||||
*/
|
||||
public class ConversationDTO {
|
||||
private Long id;
|
||||
private Long customerId;
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
/*
|
||||
* Data transfer object for coupons.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for coupons.
|
||||
*/
|
||||
public class CouponDTO {
|
||||
private Long couponId;
|
||||
private String couponCode;
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
/*
|
||||
* Data transfer object for customers.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for customers.
|
||||
*/
|
||||
public class CustomerDTO {
|
||||
@SerializedName("id")
|
||||
private Long customerId;
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
/*
|
||||
* Data transfer object used to populate dropdown menus.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for simple dropdown selection lists.
|
||||
*/
|
||||
public class DropdownDTO {
|
||||
private Long id;
|
||||
private String label;
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
/*
|
||||
* Data transfer object for employees.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for employees.
|
||||
*/
|
||||
public class EmployeeDTO {
|
||||
|
||||
private Long id;
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
/*
|
||||
* Represents an error response from the server.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Used to get messages of any errors from the backend.
|
||||
*/
|
||||
//Used to get messages of any errors from the backend
|
||||
|
||||
public class ErrorResponse {
|
||||
private String message;
|
||||
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
/*
|
||||
* Data transfer object for inventory items.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for inventory stock.
|
||||
*/
|
||||
public class InventoryDTO {
|
||||
// Response fields (from backend InventoryResponse)
|
||||
private Long inventoryId;
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
/*
|
||||
* Data transfer object for chat messages.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for chat messages.
|
||||
*/
|
||||
public class MessageDTO {
|
||||
|
||||
@SerializedName("id")
|
||||
|
||||
@@ -1,17 +1,8 @@
|
||||
/*
|
||||
* Wrapper for paginated responses from the server.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Generic response wrapper for paginated API results.
|
||||
*/
|
||||
//Used to get data from the API
|
||||
public class PageResponse<T> {
|
||||
private List<T> content;
|
||||
private int totalPages;
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
/*
|
||||
* Data transfer object for pets.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object representing a pet.
|
||||
*/
|
||||
public class PetDTO {
|
||||
private Long petId;
|
||||
private String petName;
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
/*
|
||||
* Data transfer object for products.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for products.
|
||||
*/
|
||||
public class ProductDTO {
|
||||
private Long prodId;
|
||||
private String prodName;
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
/*
|
||||
* Data transfer object for product-supplier relationships.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for mapping products to suppliers.
|
||||
*/
|
||||
public class ProductSupplierDTO {
|
||||
private Long productId;
|
||||
private String productName;
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
/*
|
||||
* Data transfer object for purchase orders.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for purchase orders.
|
||||
*/
|
||||
public class PurchaseOrderDTO {
|
||||
private Long purchaseOrderId;
|
||||
private Long supId;
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
/*
|
||||
* Data transfer object for refunds.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for refund processing.
|
||||
*/
|
||||
public class RefundDTO {
|
||||
// Response fields
|
||||
private Long id;
|
||||
|
||||
@@ -1,18 +1,8 @@
|
||||
/*
|
||||
* Data transfer object for sales.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for sales transactions.
|
||||
*/
|
||||
public class SaleDTO {
|
||||
// Response fields
|
||||
private Long saleId;
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
/*
|
||||
* Request object for sending a new chat message.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Request body for sending a new chat message.
|
||||
*/
|
||||
public class SendMessageRequest {
|
||||
|
||||
private String content;
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
/*
|
||||
* Data transfer object for services.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for services.
|
||||
*/
|
||||
public class ServiceDTO {
|
||||
private Long serviceId;
|
||||
private String serviceName;
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
/*
|
||||
* Data transfer object for store information.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for store information.
|
||||
*/
|
||||
public class StoreDTO {
|
||||
private Long storeId;
|
||||
private String storeName;
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
/*
|
||||
* Data transfer object for suppliers.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for suppliers.
|
||||
*/
|
||||
public class SupplierDTO {
|
||||
private Long supId;
|
||||
private String supCompany;
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
/*
|
||||
* Request object for changing a conversation's status.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Request body for updating chat conversation status.
|
||||
*/
|
||||
public class UpdateConversationStatusRequest {
|
||||
private String status;
|
||||
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
/*
|
||||
* Data transfer object for user account information.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for user account details.
|
||||
*/
|
||||
public class UserDTO {
|
||||
private Long id;
|
||||
private String username;
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Fragment for the real-time chat screen.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
@@ -72,9 +65,6 @@ import okhttp3.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.ResponseBody;
|
||||
|
||||
/**
|
||||
* Fragment for handling customer support chat.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickListener, StompChatManager.MessageListener,
|
||||
StompChatManager.ConversationListener, StompChatManager.ConnectionListener {
|
||||
@@ -100,9 +90,6 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
private StompChatManager stompChatManager;
|
||||
private ActivityResultLauncher<Intent> attachmentLauncher;
|
||||
|
||||
/**
|
||||
* Initializes the view model and attachment launcher.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -118,9 +105,6 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and sets up UI event listeners.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
ViewGroup container, Bundle savedInstanceState) {
|
||||
@@ -153,9 +137,6 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the logic to open and close the chat drawer.
|
||||
*/
|
||||
private void setupDrawerToggles() {
|
||||
binding.headerActiveChats.setOnClickListener(v -> {
|
||||
if (binding.rvActiveChats.getVisibility() == View.VISIBLE) {
|
||||
@@ -178,9 +159,6 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the adapters and layout managers for chat lists and message history.
|
||||
*/
|
||||
private void setupRecyclerViews() {
|
||||
activeChatAdapter = new ChatAdapter(activeChatList, this);
|
||||
binding.rvActiveChats.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
@@ -209,9 +187,6 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
setConversationActive(false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a full-screen image preview for message attachments.
|
||||
*/
|
||||
private void showFullScreenImage(Message message) {
|
||||
if (baseUrl == null || message.getId() == null) return;
|
||||
|
||||
@@ -233,9 +208,6 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the download process for a message attachment.
|
||||
*/
|
||||
private void downloadFile(Message message) {
|
||||
if (message.getId() == null) return;
|
||||
|
||||
@@ -255,9 +227,6 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the downloaded file body to the device's downloads folder.
|
||||
*/
|
||||
private void saveFileToDownloads(ResponseBody body, String fileName, String mimeType) {
|
||||
android.os.Handler mainHandler = new android.os.Handler(android.os.Looper.getMainLooper());
|
||||
new Thread(() -> {
|
||||
@@ -301,9 +270,6 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes LiveData from the ViewModel to update chat lists and messages.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getActiveChats().observe(getViewLifecycleOwner(), list -> {
|
||||
activeChatList.clear();
|
||||
@@ -334,18 +300,12 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
viewModel.getIsLoading().observe(getViewLifecycleOwner(), this::setLoading);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the visibility of the progress bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the chat header and input state if the active conversation changes.
|
||||
*/
|
||||
private void updateTitleAndStateIfActive(List<Chat> list) {
|
||||
if (activeConversationId != null) {
|
||||
for (Chat chat : list) {
|
||||
@@ -358,9 +318,6 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads initial chat data and establishes WebSocket connection.
|
||||
*/
|
||||
private void loadInitialData() {
|
||||
String token = tokenManager.getToken();
|
||||
Long currentUserId = tokenManager.getUserId();
|
||||
@@ -396,9 +353,6 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicks on a chat from the drawer to switch the active conversation.
|
||||
*/
|
||||
@Override
|
||||
public void onChatClick(Chat chat) {
|
||||
activeConversationId = Long.parseLong(chat.getChatId());
|
||||
@@ -414,9 +368,6 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
viewModel.loadMessageHistory(activeConversationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the active chat conversation.
|
||||
*/
|
||||
private void closeChat() {
|
||||
if (activeConversationId == null) return;
|
||||
|
||||
@@ -441,9 +392,6 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a text message to the active conversation.
|
||||
*/
|
||||
private void sendMessage() {
|
||||
if (activeConversationId == null) return;
|
||||
String text = binding.etMessage.getText().toString().trim();
|
||||
@@ -460,18 +408,12 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a file picker to select an attachment.
|
||||
*/
|
||||
private void selectAttachment() {
|
||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
intent.setType("*/*");
|
||||
attachmentLauncher.launch(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a preview of the selected attachment.
|
||||
*/
|
||||
private void showAttachmentPreview(Uri uri) {
|
||||
pendingAttachmentUri = uri;
|
||||
binding.layoutAttachmentPreview.setVisibility(View.VISIBLE);
|
||||
@@ -485,17 +427,11 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the currently selected attachment from the preview.
|
||||
*/
|
||||
private void removeAttachment() {
|
||||
pendingAttachmentUri = null;
|
||||
binding.layoutAttachmentPreview.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message with a file attachment.
|
||||
*/
|
||||
private void sendWithAttachment(Uri uri) {
|
||||
if (activeConversationId == null) return;
|
||||
|
||||
@@ -532,9 +468,6 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when a new message is received through the WebSocket.
|
||||
*/
|
||||
@Override
|
||||
public void onMessageReceived(MessageDTO dto) {
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
@@ -545,9 +478,6 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when a conversation's status or last message is updated.
|
||||
*/
|
||||
@Override
|
||||
public void onConversationUpdated(ConversationDTO dto) {
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
@@ -559,9 +489,6 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the WebSocket connection is successfully opened.
|
||||
*/
|
||||
@Override
|
||||
public void onSocketOpened() {
|
||||
if (!isAdded()) return;
|
||||
@@ -571,18 +498,12 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the WebSocket connection is closed.
|
||||
*/
|
||||
@Override
|
||||
public void onSocketClosed() {
|
||||
if (!isAdded()) return;
|
||||
requireActivity().runOnUiThread(viewModel::loadConversations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when a WebSocket error occurs.
|
||||
*/
|
||||
@Override
|
||||
public void onSocketError() {
|
||||
if (!isAdded()) return;
|
||||
@@ -592,9 +513,6 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls the message list to the most recent message.
|
||||
*/
|
||||
private void scrollToBottom() {
|
||||
if (!messageList.isEmpty()) {
|
||||
binding.rvMessages.post(() ->
|
||||
@@ -602,9 +520,6 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the UI state based on whether a conversation is active and its status.
|
||||
*/
|
||||
private void setConversationActive(boolean active, String status) {
|
||||
boolean isClosed = "CLOSED".equalsIgnoreCase(status);
|
||||
UIUtils.setViewsEnabled(active && !isClosed, binding.btnSend, binding.etMessage, binding.btnAttach);
|
||||
@@ -626,9 +541,6 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up resources and disconnects the WebSocket when the view is destroyed.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Base fragment that provides common list and search functionality.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
@@ -30,9 +23,7 @@ import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment that serves as a container for various list-based screens, providing a navigation drawer.
|
||||
*/
|
||||
//The Fragment for the displaying the list of entities to be viewed
|
||||
@AndroidEntryPoint
|
||||
public class ListFragment extends Fragment {
|
||||
|
||||
@@ -106,9 +97,6 @@ public class ListFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding when the view is destroyed.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Fragment for viewing and editing the user's profile.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments;
|
||||
|
||||
import android.net.Uri;
|
||||
@@ -180,18 +173,12 @@ public class ProfileFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the visibility of the progress bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding when the view is destroyed.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Fragment for browsing the activity log.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments;
|
||||
|
||||
import android.app.DatePickerDialog;
|
||||
@@ -41,9 +34,6 @@ import java.util.List;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for viewing application activity logs with various filtering options.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class ActivityLogFragment extends Fragment {
|
||||
private FragmentActivityLogBinding binding;
|
||||
@@ -56,9 +46,6 @@ public class ActivityLogFragment extends Fragment {
|
||||
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Inflates the layout, checks for admin access, and initializes ViewModel and UI components.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -82,18 +69,12 @@ public class ActivityLogFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers initial data loading after the view is created.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
viewModel.loadInitialData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new ActivityLogAdapter(logList);
|
||||
binding.recyclerViewActivityLog.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
@@ -116,9 +97,6 @@ public class ActivityLogFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up filters for logs, including search, role, store, and date range.
|
||||
*/
|
||||
private void setupFilters() {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter,
|
||||
binding.etSearchLog, binding.spinnerRoleFilter, binding.spinnerStoreFilter);
|
||||
@@ -145,9 +123,6 @@ public class ActivityLogFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a date picker dialog and updates the selected start or end date.
|
||||
*/
|
||||
private void showDatePicker(boolean isStart) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
new DatePickerDialog(requireContext(), (view, year, month, day) -> {
|
||||
@@ -166,18 +141,12 @@ public class ActivityLogFragment extends Fragment {
|
||||
}, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles store selection from the filter spinner.
|
||||
*/
|
||||
private void onStoreSelected() {
|
||||
int pos = binding.spinnerStoreFilter.getSelectedItemPosition();
|
||||
Long storeId = (pos > 0 && !storeList.isEmpty()) ? storeList.get(pos - 1).getId() : null;
|
||||
viewModel.setStoreFilter(storeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for log list updates, store options, and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getLogs().observe(getViewLifecycleOwner(), list -> {
|
||||
logList.clear();
|
||||
@@ -195,9 +164,6 @@ public class ActivityLogFragment extends Fragment {
|
||||
binding.swipeRefreshActivityLog.setRefreshing(loading));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Fragment for browsing adoption records.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments;
|
||||
|
||||
import android.graphics.Color;
|
||||
@@ -51,9 +44,6 @@ import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a list of adoptions with a calendar view and filtering.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdoptionClickListener {
|
||||
|
||||
@@ -68,18 +58,12 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Initializes the ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(AdoptionListViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and sets up UI components, calendar, and observers.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -104,9 +88,6 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for adoption list, stores, and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getAdoptions().observe(getViewLifecycleOwner(), list -> {
|
||||
adoptionList.clear();
|
||||
@@ -125,9 +106,6 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the bulk delete handler for multiple adoption record deletion.
|
||||
*/
|
||||
private void setupBulkDelete() {
|
||||
bulkDeleteHandler = new BulkDeleteHandler(
|
||||
this,
|
||||
@@ -141,9 +119,6 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads adoption data and stores when the fragment resumes.
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@@ -151,9 +126,6 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
if (!isStaff()) viewModel.loadStores();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles between month and week display modes for the calendar.
|
||||
*/
|
||||
private void toggleCalendarMode() {
|
||||
isMonthMode = !isMonthMode;
|
||||
binding.calendarViewAdoption.state().edit()
|
||||
@@ -161,9 +133,6 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the filter visibility toggle, considering user roles.
|
||||
*/
|
||||
private void setupFilterToggle() {
|
||||
if (isStaff()) {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilterAdoption, binding.layoutFilterAdoption,
|
||||
@@ -175,16 +144,10 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the currently logged-in user has the STAFF role.
|
||||
*/
|
||||
private boolean isStaff() {
|
||||
return "STAFF".equalsIgnoreCase(tokenManager.getRole());
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the calendar view for date-based filtering.
|
||||
*/
|
||||
private void setupCalendar() {
|
||||
binding.calendarViewAdoption.setOnDateChangedListener((widget, date, selected) -> {
|
||||
if (selected) {
|
||||
@@ -201,9 +164,6 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates calendar decorators to highlight dates with adoptions.
|
||||
*/
|
||||
private void updateCalendarDecorators() {
|
||||
HashSet<CalendarDay> datesWithAdoptions = new HashSet<>();
|
||||
for (AdoptionDTO adoption : adoptionList) {
|
||||
@@ -224,9 +184,6 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
binding.calendarViewAdoption.addDecorator(new EventDecorator(Color.RED, datesWithAdoptions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new AdoptionAdapter(adoptionList, this);
|
||||
binding.recyclerViewAdoptions.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
@@ -249,38 +206,23 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search input listener.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchAdoption, () -> loadAdoptions(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the status filter spinner.
|
||||
*/
|
||||
private void setupStatusFilter() {
|
||||
String[] statuses = {"All Statuses", "Completed", "Pending", "Missed", "Cancelled"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatusAdoption, statuses, () -> loadAdoptions(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the store filter spinner.
|
||||
*/
|
||||
private void setupStoreFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerStoreAdoption, () -> loadAdoptions(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshAdoption.setOnRefreshListener(() -> loadAdoptions(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads adoption data based on current filters, search query, and selected date.
|
||||
*/
|
||||
private void loadAdoptions(boolean reset) {
|
||||
String query = binding.etSearchAdoption.getText().toString().trim();
|
||||
String status = binding.spinnerStatusAdoption.getSelectedItem() != null ? binding.spinnerStatusAdoption.getSelectedItem().toString() : "All Statuses";
|
||||
@@ -308,9 +250,6 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
viewModel.loadAdoptions(reset, query, status, storeId, selectedDateString, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the adoption detail screen.
|
||||
*/
|
||||
private void openDetail(int position) {
|
||||
Bundle args = new Bundle();
|
||||
if (position != -1) {
|
||||
@@ -320,15 +259,9 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_adoption_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles adoption item clicks by opening details.
|
||||
*/
|
||||
@Override
|
||||
public void onAdoptionClick(int position) { openDetail(position); }
|
||||
|
||||
/**
|
||||
* Forwards selection changes to the bulk delete handler.
|
||||
*/
|
||||
@Override
|
||||
public void onSelectionChanged(int selectedCount) {
|
||||
if (bulkDeleteHandler != null) {
|
||||
@@ -336,9 +269,6 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Fragment for displaying store analytics and charts.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments;
|
||||
|
||||
import android.graphics.Color;
|
||||
@@ -25,9 +18,6 @@ import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Fragment for displaying business analytics, including revenue, transactions, and product performance.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class AnalyticsFragment extends Fragment {
|
||||
|
||||
@@ -41,9 +31,6 @@ public class AnalyticsFragment extends Fragment {
|
||||
private static final String[] TOP_N_OPTIONS = {"5", "10", "15", "20"};
|
||||
private static final int[] TOP_N_VALUES = { 5, 10, 15, 20 };
|
||||
|
||||
/**
|
||||
* Inflates the layout, initializes ViewModel, and sets up UI components and filters.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -64,9 +51,6 @@ public class AnalyticsFragment extends Fragment {
|
||||
private static final int COLOR_SELECTED = 0xFF4ECDC4;
|
||||
private static final int COLOR_UNSELECTED = 0xFFCBD5E1;
|
||||
|
||||
/**
|
||||
* Configures the view mode toggle buttons (My Analytics vs Store Analytics).
|
||||
*/
|
||||
private void setupViewModeToggle() {
|
||||
updateViewModeButtonStyles(viewModel.getViewMode());
|
||||
|
||||
@@ -83,9 +67,6 @@ public class AnalyticsFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the styles of the view mode buttons based on the selected mode.
|
||||
*/
|
||||
private void updateViewModeButtonStyles(String mode) {
|
||||
binding.btnMyAnalytics.setBackgroundTintList(
|
||||
android.content.res.ColorStateList.valueOf(mode.equals("mine") ? COLOR_SELECTED : COLOR_UNSELECTED));
|
||||
@@ -93,9 +74,6 @@ public class AnalyticsFragment extends Fragment {
|
||||
android.content.res.ColorStateList.valueOf(mode.equals("store") ? COLOR_SELECTED : COLOR_UNSELECTED));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the visibility of the store filter based on the user's role and selected view mode.
|
||||
*/
|
||||
private void updateStoreFilterVisibility(String mode) {
|
||||
boolean isAdmin = "ADMIN".equalsIgnoreCase(tokenManager.getRole());
|
||||
int vis = (isAdmin && mode.equals("store")) ? View.VISIBLE : View.GONE;
|
||||
@@ -103,10 +81,8 @@ public class AnalyticsFragment extends Fragment {
|
||||
binding.spinnerFilterStore.setVisibility(vis);
|
||||
}
|
||||
|
||||
// Filter Panel
|
||||
|
||||
/**
|
||||
* Configures the filter panel, including date pickers, presets, and action buttons.
|
||||
*/
|
||||
private void setupFilterPanel() {
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerTopN, TOP_N_OPTIONS);
|
||||
|
||||
@@ -135,18 +111,12 @@ public class AnalyticsFragment extends Fragment {
|
||||
binding.btnFilterReset.setOnClickListener(v -> resetFilters());
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the visibility of the filter content section.
|
||||
*/
|
||||
private void toggleFilters() {
|
||||
filtersExpanded = !filtersExpanded;
|
||||
binding.llFilterContent.setVisibility(filtersExpanded ? View.VISIBLE : View.GONE);
|
||||
binding.tvFilterToggleIcon.setText(filtersExpanded ? "▲" : "▼");
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a date range preset to the filter fields.
|
||||
*/
|
||||
private void applyPreset(int startOffset, int endOffset) {
|
||||
binding.etFilterStartDate.setText(getDateString(startOffset));
|
||||
binding.etFilterEndDate.setText(getDateString(endOffset));
|
||||
@@ -154,9 +124,6 @@ public class AnalyticsFragment extends Fragment {
|
||||
applyFiltersFromUI();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads filter values from the UI and applies them to the ViewModel.
|
||||
*/
|
||||
private void applyFiltersFromUI() {
|
||||
AnalyticsViewModel.FilterState filter = new AnalyticsViewModel.FilterState();
|
||||
filter.startDate = binding.etFilterStartDate.getText().toString().trim();
|
||||
@@ -175,9 +142,6 @@ public class AnalyticsFragment extends Fragment {
|
||||
viewModel.applyFilter(filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all filters to their default values.
|
||||
*/
|
||||
private void resetFilters() {
|
||||
binding.etFilterStartDate.setText("");
|
||||
binding.etFilterEndDate.setText("");
|
||||
@@ -188,9 +152,6 @@ public class AnalyticsFragment extends Fragment {
|
||||
viewModel.resetFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the text summary of the currently selected date range.
|
||||
*/
|
||||
private void updateFilterSummary() {
|
||||
String start = binding.etFilterStartDate.getText().toString().trim();
|
||||
String end = binding.etFilterEndDate.getText().toString().trim();
|
||||
@@ -205,16 +166,10 @@ public class AnalyticsFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a date string into a shorter version for display.
|
||||
*/
|
||||
private String shortDate(String date) {
|
||||
return (date != null && date.length() >= 10) ? date.substring(5) : date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a formatted date string for a given day.
|
||||
*/
|
||||
private String getDateString(int offsetDays) {
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.add(Calendar.DAY_OF_YEAR, offsetDays);
|
||||
@@ -222,10 +177,8 @@ public class AnalyticsFragment extends Fragment {
|
||||
c.get(Calendar.YEAR), c.get(Calendar.MONTH) + 1, c.get(Calendar.DAY_OF_MONTH));
|
||||
}
|
||||
|
||||
// ViewModel Observation
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for analytics data, loading status, errors, and filter options.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getAnalyticsData().observe(getViewLifecycleOwner(), this::computeAndDisplay);
|
||||
|
||||
@@ -263,19 +216,14 @@ public class AnalyticsFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
// Display
|
||||
|
||||
/**
|
||||
* Computes and displays analytics data in summary cards and bar charts.
|
||||
*/
|
||||
private void computeAndDisplay(AnalyticsViewModel.AnalyticsData data) {
|
||||
if (data == null) return;
|
||||
|
||||
@@ -364,10 +312,8 @@ public class AnalyticsFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
// Chart Helpers
|
||||
|
||||
/**
|
||||
* Dynamically adds a bar chart row to a given layout container.
|
||||
*/
|
||||
private void addBarRow(LinearLayout parent, String label, String value, float ratio, String color) {
|
||||
if (getContext() == null) return;
|
||||
LinearLayout row = new LinearLayout(getContext());
|
||||
@@ -413,9 +359,6 @@ public class AnalyticsFragment extends Fragment {
|
||||
parent.addView(row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an empty message row to a given layout container.
|
||||
*/
|
||||
private void addEmptyRow(LinearLayout parent, String message) {
|
||||
if (getContext() == null) return;
|
||||
TextView tv = new TextView(getContext());
|
||||
@@ -425,9 +368,6 @@ public class AnalyticsFragment extends Fragment {
|
||||
parent.addView(tv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an error message and updates UI to reflect the error state.
|
||||
*/
|
||||
private void showError(String msg) {
|
||||
if (getContext() == null || binding == null) return;
|
||||
binding.tvTotalRevenue.setText("Error");
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Fragment for browsing appointments.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments;
|
||||
|
||||
import android.graphics.Color;
|
||||
@@ -52,9 +45,6 @@ import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a list of appointments with calendar integration and filtering.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class AppointmentFragment extends Fragment implements AppointmentAdapter.OnAppointmentClickListener {
|
||||
|
||||
@@ -73,9 +63,6 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
private Long currentUserId = null;
|
||||
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
|
||||
|
||||
/**
|
||||
* Initializes ViewModels for appointment and authentication data.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -83,9 +70,6 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
authViewModel = new ViewModelProvider(this).get(AuthViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and sets up UI components, calendar, and observers.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -113,9 +97,6 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for appointment list, stores, and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getAppointments().observe(getViewLifecycleOwner(), list -> {
|
||||
appointmentList.clear();
|
||||
@@ -134,9 +115,6 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the bulk delete handler for multiple appointment deletion.
|
||||
*/
|
||||
private void setupBulkDelete() {
|
||||
bulkDeleteHandler = new BulkDeleteHandler(
|
||||
this,
|
||||
@@ -150,9 +128,6 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads appointment data and stores when the fragment resumes.
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@@ -160,9 +135,6 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
if (!isStaff()) viewModel.loadStores();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles between month and week display modes for the calendar.
|
||||
*/
|
||||
private void toggleCalendarMode() {
|
||||
isMonthMode = !isMonthMode;
|
||||
binding.calendarView.state().edit()
|
||||
@@ -170,18 +142,12 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the "My Appointments" filter button.
|
||||
*/
|
||||
private void setupMyAppointmentFilter() {
|
||||
binding.btnMyAppointments.setOnClickListener(v -> {
|
||||
loadAppointmentData(true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads information about the currently logged-in user.
|
||||
*/
|
||||
private void loadCurrentUserInfo() {
|
||||
authViewModel.getMe().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
@@ -190,9 +156,6 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the filter visibility toggle.
|
||||
*/
|
||||
private void setupFilterToggle() {
|
||||
if (isStaff()) {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchAppointment, binding.spinnerStatus);
|
||||
@@ -203,9 +166,6 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the calendar view for date-based filtering.
|
||||
*/
|
||||
private void setupCalendar() {
|
||||
binding.calendarView.setOnDateChangedListener((widget, date, selected) -> {
|
||||
if (selected) {
|
||||
@@ -222,9 +182,6 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates calendar decorators to highlight dates with appointments.
|
||||
*/
|
||||
private void updateCalendarDecorators() {
|
||||
HashSet<CalendarDay> datesWithAppointments = new HashSet<>();
|
||||
for (AppointmentDTO appointment : appointmentList) {
|
||||
@@ -243,38 +200,23 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
binding.calendarView.addDecorator(new EventDecorator(Color.RED, datesWithAppointments));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search input listener.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchAppointment, () -> loadAppointmentData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the status filter spinner.
|
||||
*/
|
||||
private void setupStatusFilter() {
|
||||
String[] statuses = {"All Statuses", "Booked", "Completed", "Cancelled", "Missed"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatus, statuses, () -> loadAppointmentData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the store filter spinner.
|
||||
*/
|
||||
private void setupStoreFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadAppointmentData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshAppointment.setOnRefreshListener(() -> loadAppointmentData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the appointment detail screen.
|
||||
*/
|
||||
private void openAppointmentDetails(int position) {
|
||||
Bundle args = new Bundle();
|
||||
if (position != -1) {
|
||||
@@ -284,17 +226,11 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_appointment_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles appointment item clicks by opening details.
|
||||
*/
|
||||
@Override
|
||||
public void onAppointmentClick(int position) {
|
||||
openAppointmentDetails(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards selection changes to the bulk delete handler.
|
||||
*/
|
||||
@Override
|
||||
public void onSelectionChanged(int count) {
|
||||
if (bulkDeleteHandler != null) {
|
||||
@@ -302,16 +238,10 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the currently logged-in user has the STAFF role.
|
||||
*/
|
||||
private boolean isStaff() {
|
||||
return "STAFF".equalsIgnoreCase(tokenManager.getRole());
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads appointment data based on current filters, search query, and selected date.
|
||||
*/
|
||||
private void loadAppointmentData(boolean reset) {
|
||||
String query = binding.etSearchAppointment.getText().toString().trim();
|
||||
String status = binding.spinnerStatus.getSelectedItem() != null ? binding.spinnerStatus.getSelectedItem().toString() : "All Statuses";
|
||||
@@ -344,9 +274,6 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
viewModel.loadAppointments(reset, query, status, storeId, selectedDateString, employeeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new AppointmentAdapter(appointmentList, this);
|
||||
binding.recyclerViewAppointments.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
@@ -369,9 +296,6 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Fragment for browsing coupons.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
@@ -34,9 +27,6 @@ import java.util.List;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a list of coupons.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class CouponFragment extends Fragment implements CouponAdapter.OnCouponClickListener {
|
||||
private FragmentCouponBinding binding;
|
||||
@@ -44,9 +34,6 @@ public class CouponFragment extends Fragment implements CouponAdapter.OnCouponCl
|
||||
private CouponAdapter adapter;
|
||||
private final List<CouponDTO> couponList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Inflates the layout, initializes ViewModel, and sets up UI components.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -72,9 +59,6 @@ public class CouponFragment extends Fragment implements CouponAdapter.OnCouponCl
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for coupon list updates and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getCoupons().observe(getViewLifecycleOwner(), list -> {
|
||||
couponList.clear();
|
||||
@@ -87,9 +71,6 @@ public class CouponFragment extends Fragment implements CouponAdapter.OnCouponCl
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new CouponAdapter(couponList, this);
|
||||
binding.recyclerViewCoupon.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
@@ -112,39 +93,24 @@ public class CouponFragment extends Fragment implements CouponAdapter.OnCouponCl
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the status filter spinner.
|
||||
*/
|
||||
private void setupStatusFilter() {
|
||||
String[] statuses = {"All Statuses", "Active", "Inactive"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatusCoupon, statuses, () -> applyFilters(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the discount type filter spinner.
|
||||
*/
|
||||
private void setupTypeFilter() {
|
||||
String[] types = {"All Types", "FIXED", "PERCENT"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerTypeCoupon, types, () -> applyFilters(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search input listener.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchCoupon, () -> applyFilters(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshCoupon.setOnRefreshListener(() -> applyFilters(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies filters and loads the coupon list.
|
||||
*/
|
||||
private void applyFilters(boolean reset) {
|
||||
String statusStr = binding.spinnerStatusCoupon.getSelectedItem() != null ?
|
||||
binding.spinnerStatusCoupon.getSelectedItem().toString() : "All Statuses";
|
||||
@@ -156,39 +122,25 @@ public class CouponFragment extends Fragment implements CouponAdapter.OnCouponCl
|
||||
binding.spinnerTypeCoupon.getSelectedItem().toString() : "All Types";
|
||||
String discountType = typeStr.equals("All Types") ? null : typeStr;
|
||||
|
||||
String search = binding.etSearchCoupon.getText() != null ? binding.etSearchCoupon.getText().toString().trim() : null;
|
||||
if (search != null && search.isEmpty()) search = null;
|
||||
viewModel.loadCoupons(reset, active, discountType, search);
|
||||
viewModel.loadCoupons(reset, active, discountType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the coupon detail screen.
|
||||
*/
|
||||
private void openDetail(long id) {
|
||||
Bundle args = new Bundle();
|
||||
args.putLong("couponId", id);
|
||||
Navigation.findNavController(requireView()).navigate(R.id.couponDetailFragment, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles coupon item clicks by opening details.
|
||||
*/
|
||||
@Override
|
||||
public void onCouponClick(CouponDTO coupon) {
|
||||
openDetail(coupon.getCouponId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the bulk delete button based on selection count.
|
||||
*/
|
||||
@Override
|
||||
public void onSelectionChanged(int count) {
|
||||
binding.btnBulkDeleteCoupons.setVisibility(count > 0 ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog for deleting multiple coupons.
|
||||
*/
|
||||
private void confirmBulkDelete() {
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle("Confirm Bulk Delete")
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Fragment for browsing customers.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
@@ -29,9 +22,6 @@ import java.util.*;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a list of customers.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class CustomerFragment extends Fragment implements CustomerAdapter.OnCustomerClickListener {
|
||||
|
||||
@@ -43,9 +33,6 @@ public class CustomerFragment extends Fragment implements CustomerAdapter.OnCust
|
||||
@Inject @Named("baseUrl") String baseUrl;
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Inflates the layout, initializes ViewModel, and sets up UI components and filters.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -58,21 +45,17 @@ public class CustomerFragment extends Fragment implements CustomerAdapter.OnCust
|
||||
setupSwipeRefresh();
|
||||
observeViewModel();
|
||||
|
||||
viewModel.resetFilters();
|
||||
viewModel.loadCustomers(true);
|
||||
|
||||
binding.fabAddCustomer.setOnClickListener(v -> openDetail(-1));
|
||||
|
||||
UIUtils.setupHamburgerMenu(binding.btnHamburgerCustomer, this);
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilterCustomer, binding.layoutFilterCustomer,
|
||||
binding.etSearchCustomer, this::applyFilters, binding.spinnerStatusCustomer);
|
||||
binding.etSearchCustomer, binding.spinnerStatusCustomer);
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for customer list updates and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getFilteredCustomers().observe(getViewLifecycleOwner(), list -> {
|
||||
customerList.clear();
|
||||
@@ -85,9 +68,6 @@ public class CustomerFragment extends Fragment implements CustomerAdapter.OnCust
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new CustomerAdapter(customerList, this);
|
||||
adapter.setBaseUrl(baseUrl);
|
||||
@@ -112,24 +92,15 @@ public class CustomerFragment extends Fragment implements CustomerAdapter.OnCust
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the status filter spinner.
|
||||
*/
|
||||
private void setupStatusFilter() {
|
||||
String[] statuses = {"All Statuses", "Active", "Inactive"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatusCustomer, statuses, this::applyFilters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search input listener.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchCustomer, this::applyFilters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies filters and triggers data reloading or filtering in ViewModel.
|
||||
*/
|
||||
private void applyFilters() {
|
||||
String query = binding.etSearchCustomer.getText().toString().trim();
|
||||
String status = binding.spinnerStatusCustomer.getSelectedItem() != null ?
|
||||
@@ -137,16 +108,10 @@ public class CustomerFragment extends Fragment implements CustomerAdapter.OnCust
|
||||
viewModel.filter(query, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshCustomer.setOnRefreshListener(() -> viewModel.loadCustomers(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the customer detail screen.
|
||||
*/
|
||||
private void openDetail(int position) {
|
||||
Bundle args = new Bundle();
|
||||
if (position != -1) {
|
||||
@@ -164,17 +129,11 @@ public class CustomerFragment extends Fragment implements CustomerAdapter.OnCust
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_customer_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles customer item clicks by opening details.
|
||||
*/
|
||||
@Override
|
||||
public void onCustomerClick(int position) {
|
||||
openDetail(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Fragment for browsing inventory items.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
@@ -39,9 +32,6 @@ import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing inventory items.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class InventoryFragment extends Fragment implements InventoryAdapter.OnInventoryClickListener {
|
||||
|
||||
@@ -53,18 +43,12 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Initializes the ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(InventoryListViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and sets up UI components, filters, and observers.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -87,9 +71,6 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for inventory list updates, store list, and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getInventory().observe(getViewLifecycleOwner(), list -> {
|
||||
inventoryList.clear();
|
||||
@@ -107,9 +88,6 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the bulk delete handler for multiple inventory item deletion.
|
||||
*/
|
||||
private void setupBulkDelete() {
|
||||
bulkDeleteHandler = new BulkDeleteHandler(
|
||||
this,
|
||||
@@ -123,27 +101,18 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads store data if necessary when the fragment resumes.
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (!isStaff()) viewModel.loadStores();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the filter visibility toggle, considering user roles.
|
||||
*/
|
||||
private void setupFilterToggle() {
|
||||
if (isStaff()) {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchInventory);
|
||||
@@ -153,30 +122,18 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the currently logged-in user has the STAFF role.
|
||||
*/
|
||||
private boolean isStaff() {
|
||||
return "STAFF".equalsIgnoreCase(tokenManager.getRole());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search input listener.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchInventory, () -> loadInventory(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the store filter spinner.
|
||||
*/
|
||||
private void setupStoreFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadInventory(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new InventoryAdapter(inventoryList, this);
|
||||
binding.recyclerViewInventory.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
@@ -199,16 +156,10 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshInventory.setOnRefreshListener(() -> loadInventory(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads inventory data based on current search query and store filter.
|
||||
*/
|
||||
private void loadInventory(boolean reset) {
|
||||
String query = binding.etSearchInventory != null ? binding.etSearchInventory.getText().toString().trim() : "";
|
||||
if (query.isEmpty()) query = null;
|
||||
@@ -227,9 +178,6 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
viewModel.loadInventory(reset, query, storeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the inventory detail screen.
|
||||
*/
|
||||
private void openDetail(InventoryDTO inv) {
|
||||
Bundle args = new Bundle();
|
||||
if (inv != null) {
|
||||
@@ -238,9 +186,6 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_inventory_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles inventory item clicks by opening details.
|
||||
*/
|
||||
@Override
|
||||
public void onInventoryClick(int position) {
|
||||
if (position >= 0 && position < inventoryList.size()) {
|
||||
@@ -248,9 +193,6 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards selection changes to the bulk delete handler.
|
||||
*/
|
||||
@Override
|
||||
public void onSelectionChanged(int selectedCount) {
|
||||
if (bulkDeleteHandler != null) {
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Fragment for browsing pets.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
@@ -40,9 +33,6 @@ import javax.inject.Named;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a list of pets.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class PetFragment extends Fragment implements PetAdapter.OnPetClickListener {
|
||||
private FragmentPetBinding binding;
|
||||
@@ -54,18 +44,12 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
@Inject @Named("baseUrl") String baseUrl;
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Initializes the view model.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(PetListViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and initializes UI components and filters.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -88,9 +72,6 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes LiveData from the ViewModel to update the list and filter options.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getPets().observe(getViewLifecycleOwner(), list -> {
|
||||
petList.clear();
|
||||
@@ -113,9 +94,6 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the handler for bulk deletion of pets.
|
||||
*/
|
||||
private void setupBulkDelete() {
|
||||
bulkDeleteHandler = new BulkDeleteHandler(
|
||||
this,
|
||||
@@ -129,9 +107,6 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes pet data and filters when the fragment is resumed.
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@@ -140,9 +115,6 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
if (!isStaff()) viewModel.loadStores();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the visibility of filters based on the user's role.
|
||||
*/
|
||||
private void setupFilterToggle() {
|
||||
if (isStaff()) {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchPet,
|
||||
@@ -154,53 +126,32 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current user has the 'STAFF' role.
|
||||
*/
|
||||
private boolean isStaff() {
|
||||
return "STAFF".equalsIgnoreCase(tokenManager.getRole());
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches search functionality to the search input field.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchPet, () -> loadPetData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the status filter spinner.
|
||||
*/
|
||||
private void setupStatusFilter() {
|
||||
String[] statuses = {"All Statuses", "Available", "Adopted", "Owned", "Pending"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatus, statuses, () -> loadPetData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the species filter spinner.
|
||||
*/
|
||||
private void setupSpeciesFilter() {
|
||||
String[] initial = {"All Species"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerSpecies, initial, () -> loadPetData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the store filter spinner.
|
||||
*/
|
||||
private void setupStoreFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadPetData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshPet.setOnRefreshListener(() -> loadPetData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers loading of pet data from the backend with current filters.
|
||||
*/
|
||||
private void loadPetData(boolean reset) {
|
||||
String query = binding.etSearchPet.getText().toString().trim();
|
||||
String status = binding.spinnerStatus.getSelectedItem() != null ? binding.spinnerStatus.getSelectedItem().toString() : "All Statuses";
|
||||
@@ -220,9 +171,6 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
viewModel.loadPets(reset, query, status, species, storeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new PetAdapter(petList, this);
|
||||
adapter.setBaseUrl(baseUrl);
|
||||
@@ -247,9 +195,6 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the profile view of a specific pet.
|
||||
*/
|
||||
private void openPetProfile(int position) {
|
||||
Bundle args = new Bundle();
|
||||
PetDTO pet = petList.get(position);
|
||||
@@ -257,24 +202,15 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_pet_profile, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the screen for adding a new pet.
|
||||
*/
|
||||
private void openPetDetails() {
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_pet_detail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicks on individual pets in the list.
|
||||
*/
|
||||
@Override
|
||||
public void onPetClick(int position) {
|
||||
openPetProfile(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the bulk delete handler when item selection changes.
|
||||
*/
|
||||
@Override
|
||||
public void onSelectionChanged(int selectedCount) {
|
||||
if (bulkDeleteHandler != null) {
|
||||
@@ -282,9 +218,6 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding when the view is destroyed.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Fragment for browsing products.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
@@ -38,9 +31,6 @@ import javax.inject.Named;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a list of products.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class ProductFragment extends Fragment implements ProductAdapter.OnProductClickListener {
|
||||
|
||||
@@ -51,18 +41,12 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
|
||||
|
||||
@Inject @Named("baseUrl") String baseUrl;
|
||||
|
||||
/**
|
||||
* Initializes the ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(ProductListViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and sets up UI components.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -82,9 +66,6 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for product list, categories, and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getProducts().observe(getViewLifecycleOwner(), list -> {
|
||||
productList.clear();
|
||||
@@ -102,9 +83,6 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads product data and categories when the fragment resumes.
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@@ -112,38 +90,23 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
|
||||
viewModel.loadCategories();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the filter visibility toggle.
|
||||
*/
|
||||
private void setupFilterToggle() {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter,
|
||||
binding.etSearchProduct, binding.spinnerCategory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search input listener.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchProduct, () -> loadProductData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the category filter spinner.
|
||||
*/
|
||||
private void setupCategoryFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerCategory, () -> loadProductData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshProduct.setOnRefreshListener(() -> loadProductData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads product data based on current filters and search query.
|
||||
*/
|
||||
private void loadProductData(boolean reset) {
|
||||
String query = binding.etSearchProduct.getText().toString().trim();
|
||||
if (query.isEmpty()) query = null;
|
||||
@@ -157,9 +120,6 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
|
||||
viewModel.loadProducts(reset, query, categoryId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new ProductAdapter(productList, this);
|
||||
adapter.setBaseUrl(baseUrl);
|
||||
@@ -183,9 +143,6 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the product detail screen.
|
||||
*/
|
||||
private void openProductDetails(int position) {
|
||||
Bundle args = new Bundle();
|
||||
if (position != -1) {
|
||||
@@ -195,17 +152,11 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_product_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles product item clicks by opening details.
|
||||
*/
|
||||
@Override
|
||||
public void onProductClick(int position) {
|
||||
openProductDetails(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Fragment for browsing product-supplier links.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
@@ -36,9 +29,6 @@ import java.util.List;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing the relationships between products and suppliers.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class ProductSupplierFragment extends Fragment
|
||||
implements ProductSupplierAdapter.OnProductSupplierClickListener {
|
||||
@@ -50,18 +40,12 @@ public class ProductSupplierFragment extends Fragment
|
||||
private ProductSupplierListViewModel viewModel;
|
||||
private BulkDeleteHandler bulkDeleteHandler;
|
||||
|
||||
/**
|
||||
* Initializes the ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(ProductSupplierListViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and sets up UI components, filters, and observers.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -83,9 +67,6 @@ public class ProductSupplierFragment extends Fragment
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for product-supplier list, products, suppliers, and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getProductSuppliers().observe(getViewLifecycleOwner(), list -> {
|
||||
psList.clear();
|
||||
@@ -108,9 +89,6 @@ public class ProductSupplierFragment extends Fragment
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the bulk delete handler for multiple product-supplier relationship deletion.
|
||||
*/
|
||||
private void setupBulkDelete() {
|
||||
bulkDeleteHandler = new BulkDeleteHandler(
|
||||
this,
|
||||
@@ -124,9 +102,6 @@ public class ProductSupplierFragment extends Fragment
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads data and filter options when the fragment resumes.
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@@ -134,26 +109,17 @@ public class ProductSupplierFragment extends Fragment
|
||||
viewModel.loadFilterData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the filter visibility toggle.
|
||||
*/
|
||||
private void setupFilterToggle() {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchPS,
|
||||
binding.spinnerProduct, binding.spinnerSupplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new ProductSupplierAdapter(psList, this);
|
||||
binding.recyclerViewPS.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
@@ -176,37 +142,22 @@ public class ProductSupplierFragment extends Fragment
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search input listener.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchPS, () -> loadData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the product filter spinner.
|
||||
*/
|
||||
private void setupProductFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerProduct, () -> loadData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the supplier filter spinner.
|
||||
*/
|
||||
private void setupSupplierFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerSupplier, () -> loadData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshPS.setOnRefreshListener(() -> loadData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads product-supplier data based on current search query and filters.
|
||||
*/
|
||||
private void loadData(boolean reset) {
|
||||
String query = binding.etSearchPS.getText().toString().trim();
|
||||
if (query.isEmpty()) query = null;
|
||||
@@ -226,9 +177,6 @@ public class ProductSupplierFragment extends Fragment
|
||||
viewModel.loadProductSuppliers(reset, query, productId, supplierId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the product-supplier detail screen.
|
||||
*/
|
||||
private void openDetail(int position) {
|
||||
Bundle args = new Bundle();
|
||||
if (position != -1) {
|
||||
@@ -239,15 +187,9 @@ public class ProductSupplierFragment extends Fragment
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_product_supplier_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles product-supplier item clicks by opening details.
|
||||
*/
|
||||
@Override
|
||||
public void onProductSupplierClick(int position) { openDetail(position); }
|
||||
|
||||
/**
|
||||
* Forwards selection changes to the bulk delete handler.
|
||||
*/
|
||||
@Override
|
||||
public void onSelectionChanged(int count) {
|
||||
if (bulkDeleteHandler != null) {
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Fragment for browsing purchase orders.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
@@ -37,9 +30,6 @@ import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a list of purchase orders.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class PurchaseOrderFragment extends Fragment
|
||||
implements PurchaseOrderAdapter.OnPurchaseOrderClickListener {
|
||||
@@ -51,18 +41,12 @@ public class PurchaseOrderFragment extends Fragment
|
||||
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Initializes the ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(PurchaseOrderListViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and sets up UI components, filters, and observers.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -80,9 +64,6 @@ public class PurchaseOrderFragment extends Fragment
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for purchase order list, stores, and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getPurchaseOrders().observe(getViewLifecycleOwner(), list -> {
|
||||
poList.clear();
|
||||
@@ -100,9 +81,6 @@ public class PurchaseOrderFragment extends Fragment
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads data and stores if necessary when the fragment resumes.
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@@ -110,9 +88,6 @@ public class PurchaseOrderFragment extends Fragment
|
||||
if (!isStaff()) viewModel.loadStores();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the filter visibility toggle
|
||||
*/
|
||||
private void setupFilterToggle() {
|
||||
if (isStaff()) {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchPO);
|
||||
@@ -122,30 +97,18 @@ public class PurchaseOrderFragment extends Fragment
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the currently logged-in user has the STAFF role.
|
||||
*/
|
||||
private boolean isStaff() {
|
||||
return "STAFF".equalsIgnoreCase(tokenManager.getRole());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search input listener.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchPO, () -> loadData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the store filter spinner.
|
||||
*/
|
||||
private void setupStoreFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new PurchaseOrderAdapter(poList, this);
|
||||
binding.recyclerViewPO.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
@@ -168,16 +131,10 @@ public class PurchaseOrderFragment extends Fragment
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshPO.setOnRefreshListener(() -> loadData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads purchase order data based on current search query and store filter.
|
||||
*/
|
||||
private void loadData(boolean reset) {
|
||||
String query = binding.etSearchPO != null ? binding.etSearchPO.getText().toString().trim() : "";
|
||||
if (query.isEmpty()) query = null;
|
||||
@@ -196,9 +153,6 @@ public class PurchaseOrderFragment extends Fragment
|
||||
viewModel.loadPurchaseOrders(reset, query, storeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the purchase order detail screen.
|
||||
*/
|
||||
private void openDetail(int position) {
|
||||
Bundle args = new Bundle();
|
||||
PurchaseOrderDTO po = poList.get(position);
|
||||
@@ -206,17 +160,11 @@ public class PurchaseOrderFragment extends Fragment
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_purchase_order_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles purchase order item clicks by opening details.
|
||||
*/
|
||||
@Override
|
||||
public void onPurchaseOrderClick(int position) {
|
||||
openDetail(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Fragment for browsing sales.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
@@ -38,9 +31,6 @@ import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a list of sales.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickListener {
|
||||
|
||||
@@ -51,9 +41,6 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Inflates the layout for this fragment.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -61,9 +48,6 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes UI components and observers after the view is created.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
@@ -95,9 +79,6 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_refund));
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes LiveData from the ViewModel to update the list and filter options.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getSales().observe(getViewLifecycleOwner(), list -> {
|
||||
saleList.clear();
|
||||
@@ -120,9 +101,6 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes lookup data when the fragment is resumed.
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@@ -130,9 +108,6 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
viewModel.loadCustomers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the visibility of filters.
|
||||
*/
|
||||
private void setupFilterToggle() {
|
||||
if (isStaff()) {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchSale,
|
||||
@@ -144,53 +119,32 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current user has the 'STAFF' role.
|
||||
*/
|
||||
private boolean isStaff() {
|
||||
return "STAFF".equalsIgnoreCase(tokenManager.getRole());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current user has the 'ADMIN' role.
|
||||
*/
|
||||
private boolean isAdmin() {
|
||||
return "ADMIN".equalsIgnoreCase(tokenManager.getRole());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the store filter spinner.
|
||||
*/
|
||||
private void setupStoreFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerStore, () -> loadSales(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the payment method filter spinner.
|
||||
*/
|
||||
private void setupPaymentMethodFilter() {
|
||||
String[] paymentMethods = {"All Payments", "Cash", "Card"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerPaymentMethod, paymentMethods, () -> loadSales(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the refund status filter spinner.
|
||||
*/
|
||||
private void setupRefundStatusFilter() {
|
||||
String[] refundStatuses = {"All Status", "Sale", "Refund"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerRefundStatus, refundStatuses, () -> loadSales(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the customer filter spinner.
|
||||
*/
|
||||
private void setupCustomerFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerCustomer, () -> loadSales(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new SaleAdapter(saleList, this);
|
||||
binding.recyclerViewSales.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
@@ -214,23 +168,14 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches search functionality to the search input field.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchSale, () -> loadSales(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshSale.setOnRefreshListener(() -> loadSales(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers loading of sale data from the backend with current filters.
|
||||
*/
|
||||
private void loadSales(boolean reset) {
|
||||
String query = binding.etSearchSale != null ? binding.etSearchSale.getText().toString().trim() : "";
|
||||
if (query.isEmpty()) query = null;
|
||||
@@ -265,9 +210,6 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
viewModel.loadSales(reset, query, paymentMethod, storeId, isRefund, customerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicks on individual sales in the list.
|
||||
*/
|
||||
@Override
|
||||
public void onSaleClick(int position) {
|
||||
if (position < 0 || position >= saleList.size()) return;
|
||||
@@ -283,9 +225,6 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_sale_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding when the view is destroyed.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Fragment for browsing services.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
@@ -46,18 +39,12 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
|
||||
private ServiceListViewModel viewModel;
|
||||
private BulkDeleteHandler bulkDeleteHandler;
|
||||
|
||||
/**
|
||||
* Initializes the ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(ServiceListViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and sets up UI components and observers.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -80,9 +67,6 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for service list updates and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getServices().observe(getViewLifecycleOwner(), list -> {
|
||||
serviceList.clear();
|
||||
@@ -95,9 +79,6 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the bulk delete handler for multiple service deletion.
|
||||
*/
|
||||
private void setupBulkDelete() {
|
||||
bulkDeleteHandler = new BulkDeleteHandler(
|
||||
this,
|
||||
@@ -111,32 +92,20 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the filter visibility toggle.
|
||||
*/
|
||||
private void setupFilterToggle() {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search input listener.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchService, () -> loadServices(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new ServiceAdapter(serviceList, this);
|
||||
binding.recyclerViewServices.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
@@ -159,25 +128,16 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshService.setOnRefreshListener(() -> loadServices(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads service data based on current filters and search query.
|
||||
*/
|
||||
private void loadServices(boolean reset) {
|
||||
String query = binding.etSearchService.getText().toString().trim();
|
||||
if (query.isEmpty()) query = null;
|
||||
viewModel.loadServices(reset, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the service detail screen.
|
||||
*/
|
||||
private void openDetail(ServiceDTO service) {
|
||||
Bundle args = new Bundle();
|
||||
if (service != null) {
|
||||
@@ -186,9 +146,6 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_service_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles service item clicks by opening details.
|
||||
*/
|
||||
@Override
|
||||
public void onServiceClick(int position) {
|
||||
if (position >= 0 && position < serviceList.size()) {
|
||||
@@ -196,9 +153,6 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards selection changes to the bulk delete handler.
|
||||
*/
|
||||
@Override
|
||||
public void onSelectionChanged(int count) {
|
||||
if (bulkDeleteHandler != null) {
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Fragment for browsing staff members.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
@@ -31,9 +24,6 @@ import java.util.*;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a list of staff members.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmployeeClickListener {
|
||||
|
||||
@@ -45,9 +35,6 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
|
||||
@Inject @Named("baseUrl") String baseUrl;
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Inflates the layout and initializes UI components, filters, and observers.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -73,9 +60,6 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes LiveData from the ViewModel to update the list and filter options.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getFilteredEmployees().observe(getViewLifecycleOwner(), list -> {
|
||||
staffList.clear();
|
||||
@@ -93,9 +77,6 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new EmployeeAdapter(staffList, this);
|
||||
adapter.setBaseUrl(baseUrl);
|
||||
@@ -120,31 +101,19 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the status filter spinner.
|
||||
*/
|
||||
private void setupStatusFilter() {
|
||||
String[] statuses = {"All Statuses", "Active", "Inactive"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatusStaff, statuses, this::applyFilters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the store filter spinner.
|
||||
*/
|
||||
private void setupStoreFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerStoreStaff, this::applyFilters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches search functionality to the search input field.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchStaff, this::applyFilters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the selected filters and triggers a data reload from the view model.
|
||||
*/
|
||||
private void applyFilters() {
|
||||
String query = binding.etSearchStaff.getText().toString().trim();
|
||||
String status = binding.spinnerStatusStaff.getSelectedItem() != null ?
|
||||
@@ -159,16 +128,10 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
|
||||
viewModel.filter(query, storeId, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshStaff.setOnRefreshListener(() -> viewModel.loadStaff(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the staff detail screen for adding or editing an employee.
|
||||
*/
|
||||
private void openDetail(int position) {
|
||||
Bundle args = new Bundle();
|
||||
if (position != -1) {
|
||||
@@ -186,17 +149,11 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_staff_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicks on individual staff members in the list.
|
||||
*/
|
||||
@Override
|
||||
public void onEmployeeClick(int position) {
|
||||
openDetail(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding when the view is destroyed.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Fragment for browsing suppliers.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
@@ -34,9 +27,6 @@ import java.util.List;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and managing a list of suppliers.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupplierClickListener {
|
||||
|
||||
@@ -46,18 +36,12 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
|
||||
private SupplierListViewModel viewModel;
|
||||
private BulkDeleteHandler bulkDeleteHandler;
|
||||
|
||||
/**
|
||||
* Initializes the ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(SupplierListViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and sets up UI components, bulk delete, and observers.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -79,9 +63,6 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for supplier list updates and loading status.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getSuppliers().observe(getViewLifecycleOwner(), list -> {
|
||||
supplierList.clear();
|
||||
@@ -94,9 +75,6 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the bulk delete handler for multiple supplier deletion.
|
||||
*/
|
||||
private void setupBulkDelete() {
|
||||
bulkDeleteHandler = new BulkDeleteHandler(
|
||||
this,
|
||||
@@ -110,39 +88,24 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the filter visibility toggle.
|
||||
*/
|
||||
private void setupFilterToggle() {
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchSupplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the search input listener.
|
||||
*/
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchSupplier, () -> loadSupplierData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the swipe-to-refresh layout.
|
||||
*/
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshSupplier.setOnRefreshListener(() -> loadSupplierData(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the supplier detail screen.
|
||||
*/
|
||||
private void openSupplierDetails(int position) {
|
||||
Bundle args = new Bundle();
|
||||
if (position != -1) {
|
||||
@@ -152,17 +115,11 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_supplier_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles supplier item clicks by opening details.
|
||||
*/
|
||||
@Override
|
||||
public void onSupplierClick(int position) {
|
||||
openSupplierDetails(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards selection changes to the bulk delete handler.
|
||||
*/
|
||||
@Override
|
||||
public void onSelectionChanged(int count) {
|
||||
if (bulkDeleteHandler != null) {
|
||||
@@ -170,18 +127,12 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads supplier data based on current search query.
|
||||
*/
|
||||
private void loadSupplierData(boolean reset) {
|
||||
String query = binding.etSearchSupplier != null ? binding.etSearchSupplier.getText().toString().trim() : "";
|
||||
if (query.isEmpty()) query = null;
|
||||
viewModel.loadSuppliers(reset, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the RecyclerView and its scroll listener for pagination.
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
adapter = new SupplierAdapter(supplierList, this);
|
||||
binding.recyclerViewSuppliers.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Detail screen for viewing and editing a single adoption.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
@@ -46,18 +39,12 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Initializes the fragment and its ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(AdoptionDetailViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for the fragment
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -65,9 +52,6 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up UI components, observers, and loads initial data after the view is created.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
@@ -84,9 +68,6 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
binding.btnDeleteAdoption.setOnClickListener(v -> confirmDelete());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up observers for ViewModel to update the UI dynamically.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getViewState().observe(getViewLifecycleOwner(), this::applyViewState);
|
||||
|
||||
@@ -132,27 +113,18 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the loading bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the spinners with data and selection listeners.
|
||||
*/
|
||||
private void setupSpinners() {
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerAdoptionStatus, new String[]{});
|
||||
|
||||
@@ -169,17 +141,11 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
SpinnerUtils.setOnIndexSelectedListener(binding.spinnerAdoptionStatus, p -> notifyDateStatusChange());
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the date picker for the adoption date field.
|
||||
*/
|
||||
private void setupDatePicker() {
|
||||
binding.etAdoptionDate.setOnClickListener(v ->
|
||||
UIUtils.showDatePicker(requireContext(), binding.etAdoptionDate, this::notifyDateStatusChange));
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the ViewModel when the date or status changes to update available options.
|
||||
*/
|
||||
private void notifyDateStatusChange() {
|
||||
if (isUpdatingUI) return;
|
||||
String date = binding.etAdoptionDate.getText().toString();
|
||||
@@ -188,9 +154,6 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
viewModel.onDateChanged(date, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses fragment arguments to determine if we are editing an existing record.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.containsKey("adoptionId")) {
|
||||
@@ -201,9 +164,6 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
viewModel.setAdoptionId(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the adoption data from the backend.
|
||||
*/
|
||||
private void loadAdoptionData() {
|
||||
viewModel.loadAdoption().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
@@ -214,10 +174,6 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the current ViewState to the UI elements.
|
||||
* This handles enabling/disabling views and setting text/selections based on state.
|
||||
*/
|
||||
private void applyViewState(AdoptionDetailViewModel.ViewState state) {
|
||||
isUpdatingUI = true;
|
||||
|
||||
@@ -268,16 +224,10 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
isUpdatingUI = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the currently logged-in user has the STAFF role.
|
||||
*/
|
||||
private boolean isStaff() {
|
||||
return "STAFF".equalsIgnoreCase(tokenManager.getRole());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input and saves the adoption record.
|
||||
*/
|
||||
private void saveAdoption() {
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerAdoptionCustomer, "Customer")) return;
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerAdoptionPet, "Pet")) return;
|
||||
@@ -325,9 +275,6 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog before deleting the adoption record.
|
||||
*/
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Adoption Record", () ->
|
||||
viewModel.deleteAdoption().observe(getViewLifecycleOwner(), resource -> {
|
||||
@@ -342,9 +289,6 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous screen.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Detail screen for viewing and editing a single appointment.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
@@ -54,27 +47,18 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Initializes the fragment and its ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(AppointmentDetailViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for the fragment
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
binding = FragmentAppointmentDetailBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up UI components, observers, and loads initial data after the view is created.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
@@ -89,18 +73,12 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
binding.btnDeleteAppointment.setOnClickListener(v -> confirmDelete());
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the spinners with data and selection listeners.
|
||||
*/
|
||||
private void setupSpinners() {
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerAppointmentStatus, new String[]{});
|
||||
|
||||
@@ -130,17 +108,11 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
SpinnerUtils.setOnIndexSelectedListener(binding.spinnerAppointmentStatus, p -> notifyDateTimeStatusChange());
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the date picker for the appointment date field.
|
||||
*/
|
||||
private void setupDatePicker() {
|
||||
binding.etAppointmentDate.setOnClickListener(v ->
|
||||
UIUtils.showDatePicker(requireContext(), binding.etAppointmentDate, this::notifyDateTimeStatusChange));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up observers for ViewModel to update the UI dynamically.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getViewState().observe(getViewLifecycleOwner(), this::applyViewState);
|
||||
|
||||
@@ -175,19 +147,12 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the loading bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the current ViewState to the UI elements.
|
||||
* This handles enabling/disabling views and setting text/selections based on state.
|
||||
*/
|
||||
private void applyViewState(AppointmentDetailViewModel.ViewState state) {
|
||||
isUpdatingUI = true;
|
||||
|
||||
@@ -236,16 +201,10 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
isUpdatingUI = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the currently logged-in user has the STAFF role.
|
||||
*/
|
||||
private boolean isStaff() {
|
||||
return "STAFF".equalsIgnoreCase(tokenManager.getRole());
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the ViewModel when the date, time, or status changes to update available options.
|
||||
*/
|
||||
private void notifyDateTimeStatusChange() {
|
||||
if (isUpdatingUI) return;
|
||||
|
||||
@@ -256,9 +215,6 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
viewModel.onDateOrTimeChanged(date, time, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses fragment arguments to determine if we are editing an existing record.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.containsKey("appointmentId")) {
|
||||
@@ -269,9 +225,6 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the appointment data from the backend.
|
||||
*/
|
||||
private void loadAppointmentData() {
|
||||
viewModel.loadAppointment().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
@@ -291,9 +244,6 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input and saves the appointment record.
|
||||
*/
|
||||
private void saveAppointment() {
|
||||
if (!validateRequiredFields()) return;
|
||||
|
||||
@@ -325,9 +275,6 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that all required fields are selected or filled.
|
||||
*/
|
||||
private boolean validateRequiredFields() {
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerCustomer, "Customer")) return false;
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerStore, "Store")) return false;
|
||||
@@ -337,24 +284,15 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the selected hour and minute from spinners into a time string.
|
||||
*/
|
||||
private String buildTimeString() {
|
||||
return DateTimeUtils.formatTime(HOURS[binding.spinnerHour.getSelectedItemPosition()], MINUTES[binding.spinnerMinute.getSelectedItemPosition()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles errors that occur during the save process.
|
||||
*/
|
||||
private void handleSaveError(String errorMessage) {
|
||||
if (errorMessage != null && errorMessage.toLowerCase().contains("not available")) showNoAvailabilityDialog();
|
||||
else Toast.makeText(getContext(), errorMessage != null ? errorMessage : "Error saving", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a dialog when the selected time slot is not available.
|
||||
*/
|
||||
private void showNoAvailabilityDialog() {
|
||||
new androidx.appcompat.app.AlertDialog.Builder(requireContext())
|
||||
.setTitle("No Availability")
|
||||
@@ -363,9 +301,6 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
.setNegativeButton("Cancel Booking", (d, w) -> navigateBack()).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog before deleting the appointment record.
|
||||
*/
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Appointment", () ->
|
||||
viewModel.deleteAppointment().observe(getViewLifecycleOwner(), resource -> {
|
||||
@@ -375,16 +310,10 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous screen.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a time string and sets the hour and minute spinners accordingly.
|
||||
*/
|
||||
private void parseAndSetTimeSpinners(String time) {
|
||||
int[] parsedTime = DateTimeUtils.parseTimeString(time);
|
||||
if (parsedTime == null) return;
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Detail screen for viewing and editing a single coupon.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
@@ -36,18 +29,12 @@ import java.util.Locale;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and editing coupon details.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class CouponDetailFragment extends Fragment {
|
||||
private FragmentCouponDetailBinding binding;
|
||||
private CouponDetailViewModel viewModel;
|
||||
private long couponId = -1;
|
||||
|
||||
/**
|
||||
* Inflates the layout, initializes ViewModel, and sets up UI components.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -87,16 +74,10 @@ public class CouponDetailFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the discount type spinner with options.
|
||||
*/
|
||||
private void setupDiscountTypeSpinner() {
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerDiscountTypeDetail, new String[]{"FIXED", "PERCENT"});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a date picker for an EditText field.
|
||||
*/
|
||||
private void setupDatePicker(android.widget.EditText editText, android.widget.EditText dependOn, Runnable onDateSet) {
|
||||
editText.setFocusable(false);
|
||||
editText.setClickable(true);
|
||||
@@ -110,9 +91,6 @@ public class CouponDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads coupon details from the backend and populates the UI.
|
||||
*/
|
||||
private void loadCouponDetails() {
|
||||
binding.tvCouponId.setText(DateTimeUtils.formatId(couponId));
|
||||
binding.tvCouponId.setVisibility(View.VISIBLE);
|
||||
@@ -133,9 +111,6 @@ public class CouponDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input and saves the coupon record.
|
||||
*/
|
||||
private void saveCoupon() {
|
||||
if (!InputValidator.isNotEmpty(binding.etCouponCodeDetail, "Coupon Code")) return;
|
||||
if (!InputValidator.isGreaterThanZero(binding.etDiscountValueDetail, "Discount Value")) return;
|
||||
@@ -181,9 +156,6 @@ public class CouponDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog before deleting the coupon.
|
||||
*/
|
||||
private void confirmDelete() {
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle("Delete Coupon")
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Detail screen for viewing and editing a single customer.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
@@ -28,9 +21,6 @@ import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and editing customer details.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
public class CustomerDetailFragment extends Fragment {
|
||||
|
||||
@@ -41,9 +31,6 @@ public class CustomerDetailFragment extends Fragment {
|
||||
|
||||
private final String[] STATUSES = {"Active", "Inactive"};
|
||||
|
||||
/**
|
||||
* Inflates the layout, initializes ViewModel, and sets up UI components.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -62,16 +49,10 @@ public class CustomerDetailFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the spinners with data.
|
||||
*/
|
||||
private void setupSpinners() {
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerCustomerStatus, STATUSES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses fragment arguments to determine if we are editing an existing record.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.getBoolean("isEditing", false)) {
|
||||
@@ -105,9 +86,6 @@ public class CustomerDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads customer data from the backend.
|
||||
*/
|
||||
private void loadCustomerData(long id) {
|
||||
viewModel.loadCustomer(id).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null) {
|
||||
@@ -127,18 +105,12 @@ public class CustomerDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the loading bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input and saves the customer record.
|
||||
*/
|
||||
private void save() {
|
||||
if (!InputValidator.isNotEmpty(binding.etCustomerUsername, "Username")) return;
|
||||
|
||||
@@ -188,9 +160,6 @@ public class CustomerDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog before deleting the customer.
|
||||
*/
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Customer", () ->
|
||||
viewModel.deleteCustomer().observe(getViewLifecycleOwner(), resource -> {
|
||||
@@ -206,16 +175,10 @@ public class CustomerDetailFragment extends Fragment {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous screen.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Detail screen for viewing and editing a single inventory item.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
@@ -44,18 +37,12 @@ public class InventoryDetailFragment extends Fragment {
|
||||
private long preselectedStoreId = -1;
|
||||
private long preselectedProductId = -1;
|
||||
|
||||
/**
|
||||
* Initializes the fragment and its ViewModel.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(InventoryDetailViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for the fragment.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -63,9 +50,6 @@ public class InventoryDetailFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up UI components, observers, and loads initial data after the view is created.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
@@ -79,35 +63,23 @@ public class InventoryDetailFragment extends Fragment {
|
||||
binding.btnDeleteInventory.setOnClickListener(v -> confirmDelete());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up observers for ViewModel to update the UI dynamically.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getStoreList().observe(getViewLifecycleOwner(), list -> refreshStoreSpinner());
|
||||
viewModel.getProductList().observe(getViewLifecycleOwner(), list -> refreshProductSpinner());
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the loading bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads initial data for the store and product spinners.
|
||||
*/
|
||||
private void loadSpinnersData() {
|
||||
viewModel.loadStores().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
@@ -125,27 +97,18 @@ public class InventoryDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the store spinner with the current data.
|
||||
*/
|
||||
private void refreshStoreSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerInventoryStore, viewModel.getStoreList().getValue(),
|
||||
DropdownDTO::getLabel, "-- Select Store --",
|
||||
preselectedStoreId, DropdownDTO::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the product spinner with the current data.
|
||||
*/
|
||||
private void refreshProductSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerInventoryProduct, viewModel.getProductList().getValue(),
|
||||
DropdownDTO::getLabel, "-- Select Product --",
|
||||
preselectedProductId, DropdownDTO::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses fragment arguments to determine if we are editing an existing record.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle args = getArguments();
|
||||
if (args != null && args.containsKey("inventoryId")) {
|
||||
@@ -168,9 +131,6 @@ public class InventoryDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the inventory data from the backend.
|
||||
*/
|
||||
private void loadInventoryData() {
|
||||
viewModel.loadInventory().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
@@ -189,9 +149,6 @@ public class InventoryDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input and saves the inventory record.
|
||||
*/
|
||||
private void saveInventory() {
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerInventoryStore, "Store")) return;
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerInventoryProduct, "Product")) return;
|
||||
@@ -219,16 +176,10 @@ public class InventoryDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog before deleting the inventory record.
|
||||
*/
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Inventory Item", this::deleteInventory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the ViewModel to delete the inventory record.
|
||||
*/
|
||||
private void deleteInventory() {
|
||||
setButtonsEnabled(false);
|
||||
viewModel.deleteInventory().observe(getViewLifecycleOwner(), resource -> {
|
||||
@@ -246,16 +197,10 @@ public class InventoryDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous screen.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the action buttons.
|
||||
*/
|
||||
private void setButtonsEnabled(boolean enabled) {
|
||||
UIUtils.setViewsEnabled(enabled, binding.btnSaveInventory, binding.btnDeleteInventory, binding.btnInventoryBack);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Detail screen for viewing and editing a single pet.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* Detail screen for viewing and editing a single product.
|
||||
*
|
||||
* Author: Alex, Nikitha
|
||||
* Date: April 2026
|
||||
*/
|
||||
|
||||
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
|
||||
import android.net.Uri;
|
||||
@@ -65,9 +58,6 @@ public class ProductDetailFragment extends Fragment {
|
||||
@Inject @Named("baseUrl") String baseUrl;
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Initializes the fragment, its ViewModel, and the ImagePickerHelper.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -98,9 +88,6 @@ public class ProductDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout for the fragment.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -108,9 +95,6 @@ public class ProductDetailFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up UI components, observers, and handles arguments after the view is created.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
@@ -124,9 +108,6 @@ public class ProductDetailFragment extends Fragment {
|
||||
binding.ivProductImage.setOnClickListener(v -> imagePickerHelper.showImagePickerDialog("Select Product Image", hasImage));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up observers for the ViewModel and loads product categories.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
viewModel.getCategoryList().observe(getViewLifecycleOwner(), list -> updateCategorySpinner());
|
||||
|
||||
@@ -139,36 +120,24 @@ public class ProductDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the loading bar.
|
||||
*/
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the category spinner with available categories.
|
||||
*/
|
||||
private void updateCategorySpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerProductCategory, viewModel.getCategoryList().getValue(),
|
||||
DropdownDTO::getLabel, "-- Select Category --",
|
||||
preselectedCategoryId, DropdownDTO::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the binding reference.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses fragment arguments to determine if we are editing an existing record.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.containsKey("prodId")) {
|
||||
@@ -189,9 +158,6 @@ public class ProductDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the product details from the backend.
|
||||
*/
|
||||
private void loadProductData() {
|
||||
viewModel.loadProduct().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
@@ -209,9 +175,6 @@ public class ProductDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the product image from the server.
|
||||
*/
|
||||
private void loadProductImage() {
|
||||
String imageUrl = baseUrl + String.format(Locale.US, ProductApi.PRODUCT_IMAGE_PATH, viewModel.getProdId());
|
||||
String token = tokenManager.getToken();
|
||||
@@ -229,9 +192,6 @@ public class ProductDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs image-related actions (removal or upload) after a successful product save.
|
||||
*/
|
||||
private void performPendingImageActions(String successMsg) {
|
||||
if (isImageRemoved) {
|
||||
viewModel.deleteProductImage().observe(getViewLifecycleOwner(), resource -> {
|
||||
@@ -254,9 +214,6 @@ public class ProductDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the selected product image to the server.
|
||||
*/
|
||||
private void uploadProductImageAndNavigate(Uri uri, String successMsg) {
|
||||
File file = FileUtils.getFileFromUri(requireContext(), uri);
|
||||
if (file == null) {
|
||||
@@ -282,9 +239,6 @@ public class ProductDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input and saves the product record.
|
||||
*/
|
||||
private void saveProduct() {
|
||||
if (!InputValidator.isNotEmpty(binding.etProductName, "Product Name")) return;
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerProductCategory, "Category")) return;
|
||||
@@ -313,9 +267,6 @@ public class ProductDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog before deleting the product.
|
||||
*/
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Product", () ->
|
||||
viewModel.deleteProduct().observe(getViewLifecycleOwner(), resource -> {
|
||||
@@ -329,9 +280,6 @@ public class ProductDetailFragment extends Fragment {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous screen.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user