570 Commits

Author SHA1 Message Date
72ad73da8c switch to localhost backend 2026-04-18 08:31:56 -06:00
e4cfd0ccb1 Merge pull request #324 from RecentRunner/refactor/backend-cleanup
backend DRY/KISS cleanup
2026-04-18 08:23:37 -06:00
1eb5c4fe12 fix compatibility regressions 2026-04-17 18:52:00 -06:00
f385217234 extract image delete to storage 2026-04-17 18:24:00 -06:00
482159fbe2 add read-only transactional annotations 2026-04-17 17:58:00 -06:00
cd1216e19f simplify controllers and utilities 2026-04-17 17:35:00 -06:00
c03e5b0f37 inject AuthenticationHelper bean 2026-04-17 17:08:00 -06:00
b8e6001089 standardize CRUD services 2026-04-17 16:40:00 -06:00
47fcf082d6 centralize StringUtils usage 2026-04-17 16:15:00 -06:00
9fc51c511d clean remaining code smells 2026-04-17 15:48:00 -06:00
f3ed5e0b9a fix review findings 2026-04-17 15:12:00 -06:00
ced651db32 externalize business constants 2026-04-17 14:43:00 -06:00
a86aa91c16 consolidate shared constants 2026-04-17 14:18:00 -06:00
84b6bac819 use shared StringUtils.trimToNull 2026-04-17 13:54:00 -06:00
b17ca4fbbd unify error handling 2026-04-17 13:31:00 -06:00
31a204fdab fix tests and silent failures 2026-04-17 13:02:00 -06:00
274986186f fix appointment cancellation 2026-04-16 09:45:21 -06:00
4e672859c3 hide chat history button 2026-04-16 09:05:28 -06:00
833e4f5783 fix navbar clipping 2026-04-16 08:57:24 -06:00
d4c7b3469e fix chat session leak 2026-04-16 08:40:38 -06:00
f03df0c4c6 redeploy 2026-04-16 08:25:08 -06:00
ef0ea93514 Merge pull request #322 from RecentRunner/android-desktop-parity
fix six app bugs
2026-04-16 08:13:06 -06:00
c6e38c3972 merge main into branch 2026-04-16 08:12:46 -06:00
4b6895ccd9 normalize pet status casing 2026-04-16 07:58:46 -06:00
417b27b909 guard stale init effects 2026-04-16 07:56:57 -06:00
a1ec3e728b fix six app bugs 2026-04-16 07:55:13 -06:00
d4ac4bface Add V9 sales seed 2026-04-16 07:23:04 -06:00
eaa519dd69 ai greets first with full context 2026-04-16 00:49:58 -06:00
4d6166a882 add order history to profile 2026-04-16 00:38:10 -06:00
26791de867 Seed activity logs, fix role filter 2026-04-16 00:34:13 -06:00
eb7d144b91 fix chat scroll behaviour 2026-04-16 00:25:32 -06:00
3c4ec5b11e fix chat ux and ai model 2026-04-16 00:11:08 -06:00
102edbdb19 fix contact form and appt ui 2026-04-15 23:37:16 -06:00
0bf2582e5c Merge pull request #320 from RecentRunner/fix-desktop-launch
Desktop fixes
2026-04-15 23:21:28 -06:00
83e268d6d4 restore activity logs endpoint 2026-04-15 23:19:22 -06:00
e193e29499 fix contact layout and chat ui 2026-04-15 23:12:23 -06:00
703402b5b6 fix chat badge on reply 2026-04-15 23:11:53 -06:00
c0be2a6903 pet owner search 2026-04-15 23:03:54 -06:00
8a9e4e75b5 fix about section spacing and text 2026-04-15 22:59:18 -06:00
2fecdca917 fix adopt search mobile layout 2026-04-15 22:51:39 -06:00
3ff7df6f88 fix chat escalation and sidebar 2026-04-15 22:46:49 -06:00
7ce22c4801 fix images and empty space 2026-04-15 22:37:51 -06:00
a675bd1d9e fix nav and pagination 2026-04-15 22:30:30 -06:00
c12e7fb8c3 fix layout and demo payment 2026-04-15 22:22:18 -06:00
deff20c057 mobile layout fixes (#319) 2026-04-15 22:01:49 -06:00
0128401486 fix mobile nav and env examples 2026-04-15 21:34:15 -06:00
437baf7ba5 fix stripe publishable key 2026-04-15 21:21:37 -06:00
895ba8e5fd Merge pull request #318 from RecentRunner/fix-customer-ws-subscription
fix customer ws subscription
2026-04-15 21:19:58 -06:00
4674a2eaaf fix customer ws subscription 2026-04-15 20:57:50 -06:00
00459a06b2 fix CORS for production 2026-04-15 18:39:00 -06:00
57d13504e1 fix websocket backend url 2026-04-15 18:32:38 -06:00
56ba097ef4 disable server compression 2026-04-15 18:20:18 -06:00
2d7931f3ec remove incompatible jackson config 2026-04-15 18:12:16 -06:00
a8154e32e4 fix JPQL pet query field 2026-04-15 18:01:23 -06:00
98287ad438 fix duplicate spring key 2026-04-15 17:25:31 -06:00
c46e6820c7 replace chat polling with websocket 2026-04-15 17:04:23 -06:00
34b78bf6c3 fix UserServiceTest constructor 2026-04-15 16:33:14 -06:00
381f75e6a9 force revision on deploy 2026-04-15 16:31:22 -06:00
55b2d76cbb fix yaml and swagger defaults 2026-04-15 16:31:22 -06:00
fe3731d4cf perf: azure deployment optimizations 2026-04-15 16:31:22 -06:00
d09b0a34f1 species service validation (#317)
* fix species-service validation

* add grooming for hamster, other

* expand reptile and other services
2026-04-15 16:25:14 -06:00
b835770cb6 hardcode stripe key 2026-04-15 16:24:58 -06:00
56b3e9932e exclude next cache from build 2026-04-15 16:21:27 -06:00
6c8ea1c993 fix stripe key 2026-04-15 16:17:51 -06:00
f48222bcda Merge pull request #316 from RecentRunner/android-desktop-parity
add pet image support
2026-04-15 16:16:14 -06:00
77793071ce add pet image support 2026-04-15 16:13:50 -06:00
7552bcf1f4 fix stripe key 2026-04-15 16:10:38 -06:00
ebbe46fc10 fix logo LCP 2026-04-15 16:09:07 -06:00
603d88c527 fix nav and color theme 2026-04-15 16:09:07 -06:00
01017a9ae7 Merge pull request #314 from RecentRunner/bug-fixes
Backend bug fixes
2026-04-15 16:08:23 -06:00
5a59c2b1e9 avatar on staff register (#315) 2026-04-15 16:06:17 -06:00
33ef68f27a decouple emails from transactions 2026-04-15 16:03:10 -06:00
cbf9c27bf3 Merge pull request #313 from RecentRunner/worktree-fix-refund-idempotency
lock all stateful mutations
2026-04-15 16:02:37 -06:00
1da991d76d lock all stateful mutations 2026-04-15 16:01:32 -06:00
b65868b4d5 user avatar in edit dialogs (#312) 2026-04-15 15:58:46 -06:00
2031ecc99c fix validation and 500 bugs 2026-04-15 15:58:34 -06:00
8b9c4b899f fix auth and logic bugs 2026-04-15 15:54:46 -06:00
b4d31b13af Merge branch 'loyalty-points' 2026-04-15 15:52:13 -06:00
65bfa1d06f rebuild stripe key 2026-04-15 15:51:06 -06:00
8b473c19f8 fix loyalty points display 2026-04-15 15:49:47 -06:00
f95e1e310d lock refunds against duplicates 2026-04-15 15:48:17 -06:00
be07381bc0 fix sale and adoption bugs 2026-04-15 15:46:46 -06:00
f3d2431dfb center navbar links (#311) 2026-04-15 15:44:45 -06:00
8261c1c750 Merge pull request #310 from RecentRunner/worktree-fix-cart-sessions
restore cart across devices
2026-04-15 15:43:42 -06:00
5a6a63eef6 rebuild stripe key 2026-04-15 15:42:59 -06:00
f1671aef2c restore cart across devices 2026-04-15 15:42:55 -06:00
4f564acd2b rebuild stripe key 2026-04-15 15:38:28 -06:00
fbf69f23dc fix stripe payment intent 2026-04-15 15:31:52 -06:00
8f8959db12 rebuild frontend 2026-04-15 15:10:22 -06:00
0ea6f1d6e3 disable validate on migrate 2026-04-15 14:58:19 -06:00
21a6c26d08 fix flyway failed migration 2026-04-15 14:52:05 -06:00
eec2dce818 update actions node 24 2026-04-15 14:24:39 -06:00
44294ea2ce fix test compilation 2026-04-15 14:20:47 -06:00
6555624b81 fix coupon analytics 2026-04-15 14:10:24 -06:00
654015103b ignore log files 2026-04-15 14:10:24 -06:00
27baaa08e9 remove log artifacts 2026-04-15 14:10:24 -06:00
1cb6caafcf activity logs to files 2026-04-15 14:10:24 -06:00
9f11f04fcf trim seed data 2026-04-15 14:10:14 -06:00
3bd1fb41ff add seed data 2026-04-15 14:10:14 -06:00
1146d3c768 fix scroll sorting 2026-04-15 14:10:14 -06:00
762f01ae71 fix deploy commands 2026-04-15 12:40:15 -06:00
64ce8ffba7 fix CI azure login 2026-04-15 12:36:07 -06:00
ea7e7e3825 Merge pull request #309 from RecentRunner/websitefinal
merge websitefinal
2026-04-15 12:26:47 -06:00
ea4b2292e8 merge main into websitefinal 2026-04-15 12:26:31 -06:00
01660ae4a1 Merge pull request #308 from RecentRunner/azure-deploy
merge azure-deploy
2026-04-15 12:25:46 -06:00
6bce625b12 point android app at azure backend 2026-04-15 08:13:53 -06:00
Nikitha
2fa7b99966 Chat Widget, changes in nav bar
Auto Scroll chat and changes in Nav bar
2026-04-15 08:12:16 -06:00
a22e47eabc point desktop app at azure backend 2026-04-15 08:10:17 -06:00
50f8606ba8 fix chat scroll jumps and move about us to home page 2026-04-15 08:07:17 -06:00
bbda43e4a0 fix proxy origin header and cors allowed origins config 2026-04-15 07:37:43 -06:00
5c85c43d5e Merge pull request #307 from RecentRunner/web-v2
merge web-v2
2026-04-15 07:07:34 -06:00
17ae8f95c0 Merge pull request #306 from RecentRunner/fix-appointment-history
fix appointment history
2026-04-15 07:04:46 -06:00
c18851fba8 fix appointment history 2026-04-15 07:04:22 -06:00
824fea942f Merge pull request #305 from RecentRunner/web-fixes
fix appointments and pagination
2026-04-15 06:53:09 -06:00
0a309598fc fix appointments and pagination 2026-04-15 06:52:49 -06:00
augmentedpotato
c37650d942 Small corrections 2026-04-15 06:39:41 -06:00
augmentedpotato
78a8992f71 Minor change to appointments page 2026-04-15 02:49:26 -06:00
augmentedpotato
dfc3d627c8 Points now subtract from costs 2026-04-15 02:44:14 -06:00
Alex
10ddb4ffdd Merge branch 'AttachmentsToChat' 2026-04-15 02:15:16 -06:00
Alex
7f5b0cceb1 fixed product supplier android 2026-04-15 02:14:20 -06:00
Alex
73b99b4aa0 added filtering to activity logs for desktop 2026-04-15 02:10:20 -06:00
Alex
0cf7144387 turned logs to laymen terms and added to android 2026-04-15 01:52:35 -06:00
47f60dc309 Merge pull request #304 from RecentRunner/web-coupons
web coupons
2026-04-15 01:39:43 -06:00
cae0f23522 Merge pull request #303 from RecentRunner/chat-ui-updates
chat UI updates
2026-04-15 01:37:52 -06:00
b029500553 unify ai and live chat 2026-04-15 01:35:54 -06:00
augmentedpotato
da115fd824 loyalty points 2026-04-15 01:34:49 -06:00
03c8033540 always show chat sidebar 2026-04-15 01:31:29 -06:00
d34c8f34ca Merge pull request #302 from RecentRunner/web-features
fix web chat features
2026-04-15 01:25:47 -06:00
638b15fb40 fix web chat features 2026-04-15 01:25:28 -06:00
Alex
7b176fe938 removed status on purchase order android 2026-04-15 01:09:56 -06:00
f9cd2ed758 fix chat scroll and button 2026-04-15 01:09:09 -06:00
873c1bbb38 fix contact form style 2026-04-15 00:56:24 -06:00
db756d95c4 add store images 2026-04-15 00:55:08 -06:00
augmentedpotato
3f5add59e8 Coupon system working properly 2026-04-15 00:54:46 -06:00
14e4f4ca72 Merge pull request #301 from RecentRunner/web-fixes
web fixes
2026-04-15 00:49:04 -06:00
Alex
6a082fc8f1 added pagenation to android for each fragment 2026-04-15 00:46:32 -06:00
0e1eb056a4 contact form with email 2026-04-15 00:46:32 -06:00
f45330a451 web issue fixes 2026-04-15 00:44:07 -06:00
3111553009 web fixes 2026-04-15 00:38:04 -06:00
51a03fe2c5 Merge branch 'main' of github.com:RecentRunner/group-2-threaded-project-petshop 2026-04-15 00:27:49 -06:00
95d4c84ed8 Merge pull request #300 from RecentRunner/web-pwreset
web password reset
2026-04-15 00:27:40 -06:00
Alex
f66eb12729 fixed issue for desktop when sending a file too large 2026-04-15 00:27:06 -06:00
bbecc168a0 Merge branch 'main' of github.com:RecentRunner/group-2-threaded-project-petshop 2026-04-15 00:18:43 -06:00
augmentedpotato
535004bced password reset 2026-04-15 00:17:26 -06:00
6788afc3d2 merge azure-deploy 2026-04-15 00:14:09 -06:00
Alex
3167e2c68b desktop chat now shows images 2026-04-15 00:00:17 -06:00
13ce9a9cc7 document BACKEND_URL swap 2026-04-14 23:46:38 -06:00
1dc87b0c06 add desktop backend config 2026-04-14 23:46:21 -06:00
Alex
5cf5265451 Merge branch 'AttachmentsToChat' 2026-04-14 23:43:39 -06:00
Alex
1114982553 added time stamp and sender name to android chat 2026-04-14 23:42:23 -06:00
Alex
7847bf19cc fixed chat messaging on same account with different devices 2026-04-14 23:34:07 -06:00
e16e7f5f22 Merge remote-tracking branch 'origin/main' into azure-deploy 2026-04-14 23:32:16 -06:00
f940ac4c10 Merge pull request #299 from RecentRunner/web-v1
Merge web-v1 into main
2026-04-14 23:29:51 -06:00
a4beb245a4 merge main 2026-04-14 23:29:46 -06:00
Alex
2151ac56cd Merge branch 'AttachmentsToChat' 2026-04-14 23:20:16 -06:00
ca3e272fd9 lazy init 2026-04-14 23:19:34 -06:00
Alex
c5a59e8de3 made admin analyics able to select store 2026-04-14 23:17:12 -06:00
Alex
1b4a96c923 added personal and store analytics 2026-04-14 23:10:03 -06:00
1b873f7012 Merge remote-tracking branch 'origin/main' into azure-deploy 2026-04-14 23:06:48 -06:00
0c9feefeb5 show error details 2026-04-14 22:56:54 -06:00
Alex
08337145f0 Changed android phone validation 2026-04-14 22:53:42 -06:00
88bf6f8428 runtime backend proxy 2026-04-14 22:52:01 -06:00
a241024ced use middleware for runtime backend proxy 2026-04-14 22:48:00 -06:00
Alex
7baa780c7f added staff and customer images to desktop 2026-04-14 22:43:24 -06:00
Alex
2b4fcfe24c maade it so sales display points earned 2026-04-14 22:33:10 -06:00
Alex
b7d6adc1cc implemented forget password for desktop 2026-04-14 22:15:15 -06:00
Alex
01cce24997 fixed chat loading issue andriod 2026-04-14 22:08:14 -06:00
Nikitha
f57fe9e233 Chat Saving
saving chat history
2026-04-14 22:02:05 -06:00
2a2c9ac707 fix lowercase image name in workflow 2026-04-14 21:37:16 -06:00
ae8b58a82a trigger CI on azure-deploy branch 2026-04-14 21:35:36 -06:00
316f6f45ed Azure deployment setup 2026-04-14 21:29:00 -06:00
Alex
49cf88f790 Merge branch 'AttachmentsToChat' 2026-04-14 21:15:19 -06:00
Alex
d9fca5b72d added forget password 2026-04-14 21:13:57 -06:00
aa9f3ad444 Merge pull request #296 from RecentRunner/easy-fixes
Logs folder and activity log defaults
2026-04-14 20:50:14 -06:00
92e5919c23 Logs folder and activity log date default 2026-04-14 20:50:03 -06:00
572461b179 Merge pull request #280 from RecentRunner/chat-fixes
Fix chat attachments and avatars
2026-04-14 20:26:03 -06:00
42fb086d02 Fix chat attachments and avatars 2026-04-14 20:25:54 -06:00
413cbae2bc Merge pull request #278 from RecentRunner/desktop-backend-fixes
Activity log, staff role, chat
2026-04-14 20:17:21 -06:00
5420fb3c9e Activity log filters, staff role, chat fix 2026-04-14 20:17:03 -06:00
41b840da96 Merge pull request #275 from RecentRunner/backend-fixes
Backend bug fixes
2026-04-14 20:03:32 -06:00
c00afd2256 Block chat injection 2026-04-14 20:03:11 -06:00
7db8e966fc Add species filtering 2026-04-14 20:03:11 -06:00
f68c23b209 Drop status column 2026-04-14 20:03:11 -06:00
2f4477d28a Fix stuck pet status 2026-04-14 20:03:11 -06:00
677959e3e6 Fix backend issues 2026-04-14 20:03:11 -06:00
d99e476860 Merge pull request #274 from RecentRunner/desktop-notifications
desktop chat notifications
2026-04-14 19:59:57 -06:00
52a3b2cd3b add desktop chat notifications 2026-04-14 19:59:42 -06:00
dcf41675e4 fix backend issues 2026-04-14 19:59:39 -06:00
5c2a29cc91 Merge pull request #268 from RecentRunner/fix/admin-account-guard
Harden admin guards
2026-04-14 16:10:10 -06:00
7194ba12e7 Harden admin guards 2026-04-14 16:09:39 -06:00
d19d633ef0 use openrouter/free model 2026-04-14 15:46:42 -06:00
4164e6ff21 Merge pull request #267 from RecentRunner/nullable-appointment-pet
nullable appointment pet
2026-04-14 15:41:18 -06:00
a8389d00be nullable petId in appointment 2026-04-14 15:39:30 -06:00
ffa84949ef Merge pull request #266 from RecentRunner/resend-email
resend email
2026-04-14 15:23:49 -06:00
d43942fb76 add rate limiting 2026-04-14 15:23:26 -06:00
0411e4be06 add email flows 2026-04-14 15:23:07 -06:00
augmentedpotato
2282f0da6f Profile image works, editing profile works, now uses first/last name 2026-04-14 15:02:57 -06:00
augmentedpotato
c5448b95c9 Age input when editing/adding pet profile 2026-04-14 13:56:21 -06:00
augmentedpotato
5dbddfdc1f Navbar fixed 2026-04-14 13:44:13 -06:00
augmentedpotato
2d27f95f7d Removed addresses, adjusted contact page 2026-04-14 13:31:56 -06:00
augmentedpotato
deccb27213 Fixes for appointments and My Pets fields. 2026-04-14 12:20:48 -06:00
7281691448 Updated the example environment file 2026-04-14 10:02:02 -06:00
augmentedpotato
505560e7da Favicon updated 2026-04-14 07:35:06 -06:00
augmentedpotato
580813792a Pet adoption appointments (currently has a small issue) 2026-04-14 07:14:54 -06:00
8f704eda15 Merge pull request #254 from RecentRunner/web-v1
Web v1
2026-04-14 07:06:21 -06:00
augmentedpotato
995088ece2 Chat now present in the bottom right. 2026-04-14 05:59:33 -06:00
augmentedpotato
4c3c11995a Cart fixes (backend), adjusted header, added footer, mobile formatting updates 2026-04-14 05:24:40 -06:00
Alex
582918e9e1 added filter to analytics desktop 2026-04-14 04:39:38 -06:00
Alex
62434020e2 added filters to desktop 2026-04-14 04:29:28 -06:00
Alex
27e570156c added filter by customer for sales to backend and android 2026-04-14 04:03:15 -06:00
Alex
0b2c7bda13 added clendar to adoptions and appointments on desktop 2026-04-14 03:48:46 -06:00
Alex
5c57cabb4f updated sales on desktop, and fixed sales with points again on back end 2026-04-14 03:32:29 -06:00
Alex
9c2300ad11 Merge branch 'main' into AttachmentsToChat 2026-04-14 01:10:14 -06:00
Alex
1f86158cef Merge branch 'main' into AttachmentsToChat 2026-04-14 00:57:16 -06:00
Alex
98bd619ba6 fixes to desktop part 1 2026-04-14 00:55:51 -06:00
5bd1933ea6 add webp support desktop 2026-04-14 00:17:43 -06:00
a1e930254f consolidate migrations 2026-04-14 00:11:13 -06:00
599104677d fix image paths 2026-04-14 00:11:07 -06:00
f4b62bc8c4 add cart points fields 2026-04-14 00:11:02 -06:00
Alex
6fb7ac7817 Merge branch 'AttachmentsToChat' 2026-04-13 22:36:53 -06:00
Alex
3b07eeaee2 seperated staff and customer on desktop 2026-04-13 22:15:41 -06:00
adc8c477c2 Merge pull request #253 from RecentRunner/images
seed images
2026-04-13 21:58:01 -06:00
f7480f5e80 localize seed image URLs to upload paths 2026-04-13 21:57:29 -06:00
Alex
52624118f6 fixed phone validation desktop 2026-04-13 21:52:53 -06:00
Alex
819109893c added dropdowns for breed desktop 2026-04-13 21:40:34 -06:00
Alex
954154e99c fixed pet busniss logic desktop 2026-04-13 21:10:35 -06:00
Alex
83507f4207 made it so staff cannot change the status of pets for desktop for adopted or owned 2026-04-13 20:49:22 -06:00
a04cdd60d6 Merge pull request #252 from RecentRunner/merge-migration
merge migration
2026-04-13 19:48:37 -06:00
e0297dff6a make ActivityLog entity immutable 2026-04-13 19:47:45 -06:00
Alex
0f65d6c242 added correct refund logic and points for sales 2026-04-13 19:46:52 -06:00
Alex
f13bb90aa0 added points to sale and logic backend 2026-04-13 19:46:28 -06:00
Alex
9dad410c54 added loyaltypoint usage to sales unfinished still needs to work with the backend 2026-04-13 18:52:07 -06:00
1b1236bfb2 seed normalization 2026-04-13 18:29:49 -06:00
Alex
40955b616b Can now edit loyalty points for customer on andriod, and pets now have breed dropdown 2026-04-13 18:25:11 -06:00
af5e3a88ea Merge pull request #251 from RecentRunner/payment-fixes
Payment safety fixes
2026-04-13 17:52:21 -06:00
b64b426406 Unique sale constraint 2026-04-13 17:49:59 -06:00
a5a1757af7 Add payment features 2026-04-13 17:49:59 -06:00
141ca34ea0 Add checkout snapshot 2026-04-13 17:49:59 -06:00
Alex
53a11449ac made sales readonly for andriod 2026-04-13 17:25:28 -06:00
Alex
cba39e8a3c Fixed phone validation for andriod 2026-04-13 17:18:46 -06:00
Alex
688a04c3bc added pending status on pets andiord, also made pets automatically switch to pending when an adoption is in pending 2026-04-13 17:12:41 -06:00
Alex
9fdb3087da Merge branch 'AttachmentsToChat' 2026-04-13 15:07:28 -06:00
Alex
facf1d588b added store column to desktop and display only logged in data 2026-04-13 15:06:04 -06:00
3000a64be3 Merge pull request #172 from RecentRunner/web-adopt-filter
Adopt page filter
2026-04-13 11:08:41 -06:00
de58b0b131 Remove XML declaration from misc.xml 2026-04-13 11:08:23 -06:00
augmentedpotato
23125418c3 Adopt page filter added 2026-04-13 10:34:26 -06:00
Alex
d9c34abcf8 fixed rotated image for pets and product as well 2026-04-13 00:30:26 -06:00
Alex
296f99900a Fix profile image squish and rotate isusse 2026-04-13 00:07:58 -06:00
Alex
227eb9cac8 added coupons to desktop app 2026-04-12 23:47:22 -06:00
Alex
1f801d7486 desktop: added confirmation to change onwer and only admins can change this 2026-04-12 20:01:06 -06:00
Alex
5f7f40f98a added closed chat section and fixed closed chat bug for desktop 2026-04-12 19:49:12 -06:00
Alex
d870475bc9 Fixed minor bugs
- in sales we can no longer select 0 product for a sale
- ActivityLogFragment is locked to admin
- Spinners are loaded aftrer a selection if the spinner depends on a parent spinner
2026-04-12 18:34:43 -06:00
Alex
077780c0c3 adjusted so only available pets for the selected store is displayed when adopting 2026-04-12 18:00:24 -06:00
Alex
42c9e96500 Modified project to use our utils on areas we arnt to manage code 2026-04-12 17:49:24 -06:00
Alex
e6e8dc1b23 Fixed Log filters and fixed chat attachment download 2026-04-12 17:28:40 -06:00
Alex
7a4c711e7f added Activitylogs andriod 2026-04-12 16:58:12 -06:00
Alex
0e9bbcbcea Merge branch 'AttachmentsToChat' 2026-04-12 01:09:25 -06:00
Alex
30f14a5152 added Forgetpassword page with no logic yet 2026-04-12 01:08:25 -06:00
Alex
a6d1c089ac Faded text for disabled spinners in staff 2026-04-12 00:51:49 -06:00
Alex
0df8212a65 staff cant change status or store for pets 2026-04-12 00:49:15 -06:00
Alex
16c832bd14 Made it so only admins can change pet owners 2026-04-12 00:37:15 -06:00
Alex
57e5b06666 Fixed Coupon to use in sales 2026-04-12 00:25:33 -06:00
Alex
54139e9e13 Used the wrong endpoint for populating species, changed to to the correct one
also added coupon option to sales
2026-04-11 23:50:22 -06:00
7f7b9d2ed7 Merge pull request #171 from RecentRunner/admin-activity-logs
Add activity logging
2026-04-11 23:32:38 -06:00
a3e1f67779 restrict activity logging to admin and staff only 2026-04-11 23:29:23 -06:00
79b4f7a3e8 Harden startup config 2026-04-11 23:10:18 -06:00
Alex
2075d0ba17 added petspecies spinner 2026-04-11 23:04:14 -06:00
Alex
9ee3436aa3 Made it so staffs can only manage their own store and they cannot see other branches data 2026-04-11 23:02:52 -06:00
299462d231 Consolidate log migrations 2026-04-11 22:54:43 -06:00
93fed57d4b Add log viewer 2026-04-11 22:54:27 -06:00
933db5304f Add activity logging 2026-04-11 22:54:23 -06:00
Alex
c503ddbbc2 added sort so appointment and adoption display the most recent created first 2026-04-11 22:18:39 -06:00
Alex
6245fa88da fixed bug on appointments where spinners was not populating the correct data 2026-04-11 22:02:06 -06:00
Alex
e7a4e2be7a replaced observer in viewmodels to observe only once to fix memleek 2026-04-11 15:46:30 -06:00
Alex
b69d1c8ce9 Added null checks for Appointment and Adoptions to make sure the spinner can load 2026-04-10 19:49:54 -06:00
Alex
6ee60164ce Added Customer CRUD 2026-04-10 19:34:28 -06:00
c11790c8b2 Merge pull request #170 from RecentRunner/fix-web-pr
Fix web
2026-04-10 12:52:44 -06:00
2db5532078 Fix web 2026-04-10 09:17:31 -06:00
acd52a5fa3 Update postman tests 2026-04-10 09:01:09 -06:00
94c9d65ee9 Fix collection 2026-04-10 08:58:44 -06:00
1a25681f36 Ignore .env files 2026-04-10 08:58:44 -06:00
9b6b04796d Add close chat and closed status 2026-04-10 08:58:44 -06:00
1cc6381e9b Sync postman 2026-04-10 08:58:44 -06:00
865cfb7bc4 Merge pull request #168 from RecentRunner/ai-chat-merge
Add AI chat
2026-04-10 08:58:23 -06:00
bd717c388e Fix duplicate openrouter config 2026-04-10 08:28:43 -06:00
d412490706 Fix sales UI 2026-04-10 08:20:25 -06:00
588ddd7fec Clean up OpenRouterService 2026-04-10 08:19:24 -06:00
973ee4c1d0 Update bot model 2026-04-10 08:18:54 -06:00
c0c984d82e Fix bot runtime 2026-04-10 08:18:54 -06:00
998f476319 OpenRouter bot fixes 2026-04-10 08:18:54 -06:00
1d2f5eab2f Add OpenRouter bot 2026-04-10 08:18:54 -06:00
augmentedpotato
50150b22b7 Web and AI chat 2026-04-10 08:18:54 -06:00
Alex
9d1ccb8e68 Merge branch 'AttachmentsToChat' 2026-04-10 07:43:24 -06:00
Alex
8fc6f4b8d1 added close chat option to chat 2026-04-10 07:36:54 -06:00
Alex
5850adedc3 fixed staff accounts and added coupons andriod 2026-04-10 07:17:19 -06:00
Alex
32e41397d4 Sales bug fix 2026-04-10 05:56:05 -06:00
Alex
9d7c577f85 added Analytics filter 2026-04-10 05:03:36 -06:00
Alex
49ee40b912 Added so adoption status can be missed and fixed adoption bugs for andriod 2026-04-10 04:31:10 -06:00
Alex
3bb399e6e4 fixed spinners to populate the correct pets in edit mode for adoptions 2026-04-10 02:58:14 -06:00
Alex
5340ddf98b added viewstates to Supplier and Product 2026-04-10 00:28:01 -06:00
Alex
0ee097e82d Fixed profile issue with camera and added viewstate to pet and service 2026-04-09 23:39:34 -06:00
be3763c94f Merge pull request #164 from RecentRunner/implement-chat-notifications
implement chat notifications
2026-04-09 23:36:36 -06:00
da95606f64 Restore Main Attachments 2026-04-09 23:28:14 -06:00
738ad0003b Defer Chat Attachments 2026-04-09 23:28:14 -06:00
801b7dc872 Implement chat features 2026-04-09 23:28:14 -06:00
de4fe97dc1 Merge pull request #160 from RecentRunner/gui-fixes
Refactor user management
2026-04-09 23:23:57 -06:00
ba8573f288 resolve merge conflict 2026-04-09 23:23:39 -06:00
ff97839cb7 fix table column bindings using lambdas 2026-04-09 23:18:14 -06:00
e3eaeb0b99 fix duplicate refresh in StaffAccountsController 2026-04-09 23:16:24 -06:00
ef0f46c621 Merge pull request #163 from RecentRunner/stripe-payment
Stripe Payments
2026-04-09 23:12:14 -06:00
5c8c11b03f move stripe keys to .env 2026-04-09 23:01:41 -06:00
014a70b8fc fix user id getter in completeCheckout 2026-04-09 22:57:00 -06:00
ae0ccfd45b fix stripe payment flow 2026-04-09 22:52:57 -06:00
82c39f3993 Merge pull request #162 from RecentRunner/AttachmentsToChat
Attachments to Chat
2026-04-09 22:34:59 -06:00
augmentedpotato
4d91d8b331 Stripe Payment 2026-04-09 22:27:03 -06:00
5b3961064e Merge pull request #161 from RecentRunner/table-fixes
Merge Table Fixes
2026-04-09 21:49:18 -06:00
Alex
1dd350fc8a cleaning code 2026-04-09 21:16:11 -06:00
ef5054e8e3 Restore Table Layout 2026-04-09 21:12:04 -06:00
Alex
c2faeb06ce made chat more user frendly 2026-04-09 19:47:43 -06:00
Alex
9b4aad0c36 fixed sending message with attachments 2026-04-09 18:55:12 -06:00
793053a621 Space Out Tables 2026-04-09 17:57:01 -06:00
0b25e0422a Add Sales Scroll 2026-04-09 17:39:52 -06:00
Alex
f3932b226d added attachments to chat 2026-04-09 17:39:45 -06:00
82e8e04e55 Shrink Sales View 2026-04-09 17:36:31 -06:00
51d063f95c Format Appointment Times 2026-04-09 17:29:18 -06:00
8b4e39416b Refine Desktop Pricing 2026-04-09 17:29:03 -06:00
1205459e53 Refine GUI Behavior 2026-04-09 15:51:21 -06:00
Alex
3db45bde6c fixed bug again 2026-04-09 15:37:24 -06:00
Alex
bc55580831 bug fix 2026-04-09 15:24:20 -06:00
Alex
75341c93d8 deleted unused viewmodels 2026-04-09 15:17:11 -06:00
Alex
38b830509f refactored viewmodels for listfragments 2026-04-09 14:44:04 -06:00
Alex
863692c058 created viewmodels for detailFragments 2026-04-09 14:17:51 -06:00
Alex
30ff2fe04e Merge branch 'main' into AttachmentsToChat 2026-04-09 13:44:24 -06:00
675bf36908 Refactor user management 2026-04-09 12:28:33 -06:00
39fdf8814a Adjust Sales Layout 2026-04-09 11:49:55 -06:00
3ddc9fdf92 Improve Desktop Tables 2026-04-09 11:49:14 -06:00
0cc160a02c Unify Table Behavior 2026-04-09 11:48:30 -06:00
70c883b01b Merge pull request #158 from RecentRunner/remove-sidebar-emojis
Remove sidebar emojis
2026-04-09 11:11:28 -06:00
0930bfcc9a Remove sidebar emojis 2026-04-09 11:10:59 -06:00
9a764222c4 Merge pull request #156 from RecentRunner/early-fixes
Merge Early Fixes
2026-04-09 10:39:53 -06:00
Alex
4664fe177b fixed spinner infinite loop in appointments 2026-04-09 02:48:55 -06:00
Alex
992da24260 split viewmodels for appointments 2026-04-09 02:07:35 -06:00
Alex
2d1c1f8a46 Moved appointments businiss logic to modelview andriod 2026-04-09 00:55:00 -06:00
Alex
071973e787 small change 2026-04-08 21:30:40 -06:00
Alex
05a19ac7c0 Appointments should be fully user frendly now 2026-04-08 20:14:52 -06:00
Alex
e41c4d41d0 helper class added to enable and disable fields 2026-04-08 19:57:04 -06:00
Alex
9998b31ab4 updated backend so booked appointment automatically changes to completed 2026-04-08 19:16:18 -06:00
Alex
ccb0d0dc14 fixing dropdowns 2026-04-08 18:10:18 -06:00
Alex
1e37f25a7a update dropdowns to use backend dropdown endpoints part 1 2026-04-08 17:34:33 -06:00
Alex
8d5d6f3872 making appointment userfrendly part1 andriod 2026-04-08 16:53:42 -06:00
76f22ae16a Update early fixes 2026-04-08 16:43:50 -06:00
be9f07236a Merge pull request #155 from RecentRunner/AttachmentsToChat
Merged attachments branch
2026-04-08 13:44:13 -06:00
3beb4105ea Merge main branch 2026-04-08 13:43:20 -06:00
e9d5c701f0 Fix adoption dialog 2026-04-08 11:23:02 -06:00
78da8716af Show store dropdown 2026-04-08 11:23:02 -06:00
3b9e1374f3 Update pet dialog 2026-04-08 11:23:02 -06:00
28f9fdcff6 Create adoption sale 2026-04-08 11:23:02 -06:00
0fdd603232 Fix dialog issues 2026-04-08 11:23:02 -06:00
8fb4c82a67 Fix desktop chat 2026-04-08 11:23:02 -06:00
559f3bc343 fix empty desktop lists 2026-04-08 08:18:01 -06:00
9e83d7929b restrict adoption pets 2026-04-08 08:11:20 -06:00
930e561ce0 fix desktop chat 2026-04-08 08:07:17 -06:00
39f4b6bd8a remove debit payments 2026-04-08 07:57:39 -06:00
f2932d80c8 fix desktop forms 2026-04-08 07:55:55 -06:00
2ddf5d3249 fix web appointments 2026-04-08 07:17:48 -06:00
656311f185 Merge branch 'main' into web-more-fixes-for-wednesday 2026-04-08 07:17:26 -06:00
Alex
a76dfe4d9c updated sales to have new backend data 2026-04-08 03:19:16 -06:00
Alex
4ad94a318f converted new fragments to use hilt, MVVM and jetpack nav 2026-04-08 02:22:34 -06:00
Alex
f908169bcf Converted merged fragments to viewbinding 2026-04-08 02:06:07 -06:00
Alex
30ae416ba4 Added hamburger menu helperfunction 2026-04-08 01:50:18 -06:00
Alex
3e01ad07cd added filtering for Sales and added helper method for setting up filtertoggle andriod 2026-04-08 01:32:34 -06:00
Alex
4fc33fedf4 fix minor bugs and UI inconsistancy 2026-04-08 00:22:25 -06:00
augmentedpotato
2469c07fef Can now add pets in the appointments page. 2026-04-07 23:53:48 -06:00
augmentedpotato
83477904be Feature parity with admins and users (also a minor backend change) 2026-04-07 23:23:05 -06:00
augmentedpotato
ffef9243dd Fixed(?) being unable to create appointments on today's date 2026-04-07 22:44:50 -06:00
26f9f8c0d8 fix image error responses 2026-04-07 22:41:32 -06:00
d1e77f8ef3 revert adoption fragment 2026-04-07 22:17:15 -06:00
f54bc906f1 Merge branch 'morefiles' 2026-04-07 21:43:20 -06:00
888591c970 finish android merge wiring 2026-04-07 21:15:31 -06:00
60f344d207 fix backend merge conflicts 2026-04-07 21:13:47 -06:00
507314b7e3 merge origin/main into morefiles, resolve all conflicts 2026-04-07 20:29:54 -06:00
Alex
6eddcc49ec added filter by date for adoptions to backend 2026-04-07 18:34:08 -06:00
Alex
9498128ab1 added helper method for filter spinners to maintain code 2026-04-07 18:12:02 -06:00
Alex
492591752d added adoption search and filter andriod and backend 2026-04-07 18:06:07 -06:00
Alex
31df67ef33 fixed purchase order for android on new backend 2026-04-07 17:31:43 -06:00
Alex
ce5adccdfe Merge branch 'main' into AttachmentsToChat 2026-04-07 16:24:17 -06:00
Alex
01f5efa991 added bulk delete for ProductSupplier, appointments, and adoptions 2026-04-07 16:20:51 -06:00
fa4529e123 fix alias folder ordering and request bodies in Postman collection 2026-04-07 16:16:11 -06:00
2f369c0b17 fix audit report mismatches across backend and android 2026-04-07 16:06:44 -06:00
Alex
713e919c10 bluk delete added for Service and suppliers on andriod 2026-04-07 15:24:25 -06:00
b423912a9a update postman collection 2026-04-07 15:19:25 -06:00
Alex
93b4ad8c50 added helper class for bulk delete and mad pets have bulk delete 2026-04-07 15:13:15 -06:00
Alex
0813bb4b44 Did the same to inventory 2026-04-07 14:55:43 -06:00
ada9f7fcf9 update web packages 2026-04-07 14:36:20 -06:00
Alex
679c451c04 updard Adoptions in andriod for new backend 2026-04-07 14:35:57 -06:00
Alex
baa143ff00 edited adapters in andriod to use viewbinding 2026-04-07 14:17:24 -06:00
89c706b893 stabilize desktop chat 2026-04-07 09:38:17 -06:00
7980a7b930 fix desktop chat 2026-04-07 09:34:31 -06:00
3d0e7011c1 fix desktop user inventory crud 2026-04-07 09:31:26 -06:00
f0025886e9 fix desktop adoption save 2026-04-07 09:27:34 -06:00
0cb2ecff02 add my pets api 2026-04-07 09:15:01 -06:00
4d244cc1c5 fix web registration 2026-04-07 09:10:11 -06:00
d3563e1f75 fix desktop appointments 2026-04-07 09:05:08 -06:00
Nikitha
d3b9d28513 loading employee in appointments and adoptions
changes in backend and android
2026-04-07 08:43:49 -06:00
dee517593c Merge pull request #146 from RecentRunner/AttachmentsToChat
AttachmentsToChat
2026-04-07 08:23:47 -06:00
e497abb09d merge main 2026-04-07 08:23:23 -06:00
98a589ec33 update postman collection 2026-04-07 08:17:41 -06:00
Alex
8261cdfc2d added an api connection to Users in Andriod
NOTE Will have to change backend so staffs can access other staffs
2026-04-07 07:46:22 -06:00
Alex
b4c1175ee1 fixed creating adoption for the backend and implemented adoption to andriod for changes 2026-04-07 07:27:37 -06:00
0f0a72455b Merge pull request #145 from RecentRunner/AttachmentsToChat
AttachmentsToChat
2026-04-07 06:53:07 -06:00
Alex
0a55014f21 added my appointments button for logged in user on andriod 2026-04-07 06:48:36 -06:00
Alex
094c2d4a48 added filter options to appointments in the backend and andriod 2026-04-07 06:34:28 -06:00
Alex
9bab45f04b updated Appointments on andriod for new backend 2026-04-07 06:14:17 -06:00
Alex
195c4605f0 changed backend so can sortBy productName and added search to productSupplier 2026-04-07 05:48:24 -06:00
Alex
1990022c1e Added filter by store for inventory in back end and added search to inventory 2026-04-07 05:24:25 -06:00
Alex
ef5651d468 updated inventory backend to have filter by store and added more search features to andriod 2026-04-07 05:09:48 -06:00
Alex
863a85472f updated search to call api for supplier 2026-04-07 04:19:51 -06:00
Alex
37bd69c6f1 updated search for service to call api 2026-04-07 04:12:29 -06:00
Alex
d37202edae Updated Filterdropdown design for pets 2026-04-07 03:51:09 -06:00
Alex
0086bb4a5e added more filter options to pets 2026-04-07 03:24:55 -06:00
Alex
6164a8746d changed filtering and search in pets to use api calls 2026-04-07 02:46:00 -06:00
Alex
867322b462 changed petDetailFragment to support new backend 2026-04-07 02:23:58 -06:00
Alex
fdc6f62441 fixed pet DTO and how it interacts with new backend 2026-04-07 00:27:17 -06:00
Alex
12c7384951 Fixed backend missing file issue 2026-04-07 00:10:42 -06:00
Alex
2b097cf4a9 Merge branch 'main' into AttachmentsToChat 2026-04-06 23:27:47 -06:00
Alex
c86fbedd6f Fixed seeding for backend 2026-04-06 23:27:23 -06:00
Nikitha
ff53e4a1ec Employee files
Add, edit employee (staff and admin)
2026-04-06 22:56:40 -06:00
Nikitha
74472976d5 Sale, refund and analytics documents
view sale history  and refund for sales
analytics of sale , employee and store performances
2026-04-06 22:55:45 -06:00
e3ebb93dd2 seed stores and suppliers before products on fresh DB 2026-04-06 21:18:30 -06:00
e1405984c4 Merge pull request #144 from RecentRunner/backend-refactor
backend-refactor
2026-04-06 21:13:48 -06:00
cf338920dd fix local seed: add missing categories and storeId in inventory insert 2026-04-06 21:13:17 -06:00
a6485b1519 Merge pull request #143 from RecentRunner/backend-refactor
Fix sale inventory and switch to port 3306
2026-04-06 21:06:08 -06:00
1187c7bcc1 point to port 3306 Petstoredb 2026-04-06 21:06:01 -06:00
e631ae2953 scope inventory lookup by store on sale 2026-04-06 21:01:20 -06:00
bad16acefb Merge pull request #142 from RecentRunner/backend-refactor
Fix lazy loading
2026-04-06 20:57:44 -06:00
2795511a41 fix lazy loading on me, services, refunds 2026-04-06 20:56:14 -06:00
07c370983d Merge pull request #141 from RecentRunner/backend-refactor
Backend refactor
2026-04-06 20:51:44 -06:00
2420453daa enable Hibernate validation 2026-04-06 20:46:27 -06:00
dac5f8c4a6 add activityLog store FK 2026-04-06 20:39:08 -06:00
6bde4f4e47 add message attachment fields 2026-04-06 20:38:29 -06:00
31a4356d83 add service species collection 2026-04-06 20:34:03 -06:00
a4ed9a7afc add sale channel coupon cart columns 2026-04-06 20:32:05 -06:00
682bd12873 add coupon cart cartItem entities 2026-04-06 20:30:11 -06:00
969fbdfe8b add adoption sourceStore FK 2026-04-06 20:28:31 -06:00
3f6dc132f4 add purchaseOrder store FK 2026-04-06 20:26:42 -06:00
f86cf72dd9 add storeLocation imageUrl 2026-04-06 20:25:33 -06:00
a74e2ac0ef add store dimension to inventory 2026-04-06 20:24:23 -06:00
0482af966e simplify appointment to single pet 2026-04-06 20:22:26 -06:00
2360dc2419 merge customer/employee into users 2026-04-06 20:17:27 -06:00
824ed7e5eb expand User entity fields 2026-04-06 19:49:38 -06:00
24b11e4152 switch to target DB config 2026-04-06 19:47:18 -06:00
3e74cdd25e Add target DB setup 2026-04-06 19:37:15 -06:00
Alex
0a659fad9e Merge branch 'main' into AttachmentsToChat 2026-04-06 16:29:10 -06:00
7ffea449f7 Fix brittle migrations by replacing hardcoded IDs with robust subqueries 2026-04-06 16:25:45 -06:00
Alex
7f62331773 Merge branch 'main' into AttachmentsToChat 2026-04-06 16:13:50 -06:00
038afc415a Merge pull request #139 from RecentRunner/pet-owner-store
Pet owner store
2026-04-06 16:12:09 -06:00
28e53a4379 Seed pets and appointments 2026-04-06 15:50:52 -06:00
b2f291f256 Update pet desktop 2026-04-06 15:50:49 -06:00
0cc4a2bedd Pet owner and store 2026-04-06 15:50:45 -06:00
Alex
7673504d9e Merge branch 'main' into AttachmentsToChat 2026-04-06 15:39:38 -06:00
a66a779bcb Merge pull request #138 from RecentRunner/employee-phase
Employee phase
2026-04-06 13:37:38 -06:00
cd5dd32c73 Update Postman collection 2026-04-06 13:35:01 -06:00
b5b8290131 Fix Flyway migration 2026-04-06 13:35:01 -06:00
Alex
23d765c6b5 changed detailed fragment to fill data from the backend 2026-04-06 03:12:42 -06:00
419e5302f6 Fix availability checks 2026-04-06 01:51:58 -06:00
661c9b006a Add Missed status 2026-04-06 00:39:37 -06:00
9ea5efe44e Fix employee time conflicts 2026-04-06 00:18:49 -06:00
b70afd66aa Allow cross-store staff selection 2026-04-05 23:58:21 -06:00
a3d454e119 Enforce pet ownership rules 2026-04-05 23:35:05 -06:00
Alex
d62113c0f5 Implemented View Binding to reduce code
- project uses view binding now so we don have to do
getViewbyId to refer to the xml
2026-04-05 22:50:25 -06:00
Alex
1137688d60 Edited RetrofitUtils to also call enqueue to reduce code in repository 2026-04-05 21:57:53 -06:00
Alex
b14e318df2 Fixed bug where it navigates back to petprofile after deleting the pet 2026-04-05 21:40:43 -06:00
Alex
3555b3d2a1 Refactored more of the project to MVVM and created helper class RetrofitUtil to reduce redundent code 2026-04-05 21:27:32 -06:00
Alex
c99d9d21f0 Created help class for displaying diolog and removed redundent code 2026-04-05 18:38:46 -06:00
Alex
6d990fbc63 Created Spinner Helper class and removed reducdent code 2026-04-05 18:15:36 -06:00
Alex
e354592c47 remove dead code 2026-04-05 17:48:14 -06:00
Alex
768103cbac Added Helper class and commented most fragments 2026-04-05 17:16:40 -06:00
521537dc8f Enforce staff-only assignments 2026-04-05 16:17:58 -06:00
153ec836cf Restrict assignments to staff 2026-04-05 16:03:29 -06:00
890391f982 Allow admin ownership bypass 2026-04-05 16:01:46 -06:00
1f343f4132 Harden assignment rules 2026-04-05 15:51:11 -06:00
5d95613786 Harden staff assignment 2026-04-05 12:17:37 -06:00
Alex
453cb54f19 Refactored Andriod project to use MVVM structure (Need to apply this so sales too after merge)
- Used MVVM structure so fragments are not doing all the operation from views to data and calls
- organized the structure of our proejct
2026-04-04 23:35:38 -06:00
Alex
44877cd4ad fix photo loading issue on pets and products 2026-04-04 21:21:25 -06:00
Alex
f59624f9c3 integrated Jetpack navigation to project so we dont have to manually code the functionallities of loading to different fragments 2026-04-04 20:08:40 -06:00
Alex
eee724d4f5 fixed retrofit client, but will delete this file after merges
- kept class so nothing will break when merge
- then delete after merge buy making other files use Hilt
2026-04-04 18:23:15 -06:00
Alex
1516b92a6c integrated hilt so we dont have to manually pass context and inject retrofit in andriod 2026-04-04 18:15:05 -06:00
Alex
933a6bff6b Added calendar view to adoptions in andriod 2026-04-04 17:16:44 -06:00
Alex
313ec4a57b Added enter send message and login for andriod feilds 2026-04-04 16:54:29 -06:00
072c9aadea Merge pull request #135 from RecentRunner/clean-demo-branch
Protect appointment visibility
2026-04-04 16:28:34 -06:00
1079abf0c5 Harden appointment dialog 2026-04-04 16:24:09 -06:00
109f967435 Fix appointment ownership 2026-04-04 16:24:09 -06:00
9a110d377f Hide adopted pets 2026-04-04 16:24:09 -06:00
1043ac096f Merge pull request #134 from RecentRunner/main
Update branch
2026-04-04 16:11:26 -06:00
721ec1c5ce Merge pull request #133 from RecentRunner/AttachmentsToChat
Attachments to chat
2026-04-04 16:10:39 -06:00
Alex
5fa9cfd5d6 added calendar view to appointments
- NOTE: may have to change appointments abit after backend is updated
2026-04-03 19:37:43 -06:00
b86d03d399 Merge pull request #108 from RecentRunner/web-more-fixes
Web more fixes
2026-04-03 18:13:18 -06:00
augmentedpotato
63162487b5 Fix profile images 2026-04-03 15:30:43 -06:00
augmentedpotato
99855a6e99 Fix pet sorting 2026-04-03 15:21:15 -06:00
augmentedpotato
781eb48ca9 Fix item loading 2026-04-03 15:07:41 -06:00
augmentedpotato
a76895434d Improve auth flows 2026-04-03 14:52:32 -06:00
augmentedpotato
3ee59521fd Fix web routing 2026-04-03 14:48:24 -06:00
Alex
8401d9ef62 Added images to products for android
- also added the option to delete the images to profile and pets
2026-04-02 19:21:55 -06:00
Alex
0216435221 made it so we can put attachments to chat
- Sending not implemented until backend is complete
2026-04-02 18:23:49 -06:00
6afda8e7b8 Automate reset cleanup 2026-04-01 20:17:30 -06:00
c0f44842f7 Remove duplicate migration 2026-04-01 20:08:39 -06:00
3efb285e17 Integrate refund logic 2026-04-01 19:59:03 -06:00
a45a437d39 Merge pull request #75 from RecentRunner/fix-features-icons-v2
Finalize feature fixes
2026-04-01 19:31:28 -06:00
14e3b89baf Add desktop icons 2026-04-01 18:10:20 -06:00
2ac2ce339f Fix phone formatting 2026-04-01 18:08:37 -06:00
e1f6d8cae2 Apply service logic 2026-04-01 16:57:27 -06:00
3b31ef4020 Merge migration fixes 2026-04-01 16:55:36 -06:00
6f0eab23ee Revert "Merge pull request #55 from RecentRunner/backend-normalize-users-payments"
This reverts commit 5bd836719b, reversing
changes made to f944124972.
2026-03-30 09:58:02 -06:00
2a0f5e760c Fix migration versions 2026-03-30 09:57:46 -06:00
1c8d87ca54 Merge pull request #60 from RecentRunner/web-products
Web products
2026-03-30 09:51:34 -06:00
84c70a1568 Merge remote-tracking branch 'origin/main' into web-products 2026-03-30 09:50:57 -06:00
686bf1793f Merge pull request #59 from RecentRunner/web-index
Web index
2026-03-30 09:48:50 -06:00
86841b035f Merge pull request #58 from RecentRunner/refund-layout-spacing
Refund polish
2026-03-30 09:41:16 -06:00
4ef913dfd0 Fix refund display 2026-03-30 09:40:22 -06:00
a3851871c7 Stabilize refunds 2026-03-30 09:17:22 -06:00
33c9555564 Polish sales tables 2026-03-30 09:16:52 -06:00
augmentedpotato
00c5198c47 Appointments, account stuff, adopt a pet changes 2026-03-30 05:38:15 -06:00
410b68520a Fix android backend url 2026-03-30 00:03:27 -06:00
2d4e1be832 Merge pull request #57 from RecentRunner/staff-self-analytics
Staff analytics
2026-03-29 23:55:33 -06:00
78aac62138 Fix staff analytics 2026-03-29 23:50:31 -06:00
d2a6332633 Show staff analytics 2026-03-29 23:34:52 -06:00
9c7f931df2 Scope staff analytics 2026-03-29 23:34:43 -06:00
7720fb6c34 Merge remote-tracking branch 'origin/FixedUIConsistancy' 2026-03-29 23:22:43 -06:00
4a68c99c4c Merge pull request #56 from RecentRunner/expand-pets-products-data
Expand catalog
2026-03-29 23:08:37 -06:00
b18599c280 Tighten seed filters 2026-03-29 23:07:16 -06:00
d5fdee10d5 Add pet product filters 2026-03-29 22:54:25 -06:00
01550bac30 Expand pet product data 2026-03-29 22:54:16 -06:00
5bd836719b Merge pull request #55 from RecentRunner/backend-normalize-users-payments
Normalize users
2026-03-29 22:40:13 -06:00
2cacf1f852 Clean up customer accounts 2026-03-29 22:37:18 -06:00
896f500552 Preserve backfill emails 2026-03-29 22:14:53 -06:00
b5efed880d Disable generated user accounts 2026-03-29 22:09:39 -06:00
edbaabb42b Tighten backfill migration 2026-03-29 22:02:44 -06:00
14ca0d8809 Tighten user linking 2026-03-29 21:59:43 -06:00
909026143d Fix user linking 2026-03-29 21:52:45 -06:00
Alex
67f77f4b19 added pet images to petfragment and changed other views to look consistant 2026-03-29 21:47:49 -06:00
0c173060a8 Remove debit payment data 2026-03-29 21:44:10 -06:00
d8622df318 Backfill user accounts 2026-03-29 21:44:06 -06:00
f944124972 Merge pull request #54 from RecentRunner/backend-fixes-41-49
Fix backend appointments and chat
2026-03-29 21:24:36 -06:00
5d490d7d05 Remove chat close wrapper 2026-03-29 21:14:53 -06:00
72b423c8ad Add appointment tests 2026-03-29 21:07:14 -06:00
36ac309442 Update chat conversation status 2026-03-29 21:07:10 -06:00
3b84eff536 Fix appointment overlap rules 2026-03-29 19:02:19 -06:00
ab97a86977 Add chat close endpoint 2026-03-29 19:02:12 -06:00
c37a2fdf45 Merge pull request #53 from RecentRunner/nomorebreaking
Merging in Nikitha's work
2026-03-29 18:21:56 -06:00
c397bfcad1 Fix status bar and navigation bar layout 2026-03-29 18:20:10 -06:00
d66508137b Fix Android app connection and timeout issues
Add proper timeout configuration to OkHttpClient (30s connect/read/write)
Update OkHttp logging-interceptor to 4.12.0 to match OkHttp version
Improve error messages to show server URL for debugging
Configure backend to listen on all interfaces (0.0.0.0)
Remove EdgeToEdge calls that interfered with layout
2026-03-29 17:54:23 -06:00
augmentedpotato
4dd57e3484 Merge branch 'web-index' 2026-03-29 17:47:33 -06:00
55caf434d6 Merge main into nomorebreaking 2026-03-29 17:07:35 -06:00
fdf438f3dd Merge pull request #52 from RecentRunner/desktop-pet-product-pictures
Add desktop pet and product images
2026-03-29 16:58:06 -06:00
93e71434df fix desktop pet and product dialogs 2026-03-29 16:54:01 -06:00
8c6a53250a add desktop pet and product images 2026-03-27 10:07:37 -06:00
Alex
8272c49b44 Changed android app icon 2026-03-26 22:45:28 -06:00
Alex
38e1a29f34 Added petprofile images and uploads in petprofilefragment 2026-03-26 22:07:51 -06:00
Alex
5d8d37dee4 Merge branch 'MorePushNotification' 2026-03-26 21:39:38 -06:00
Alex
2c61e6e664 Make chat notification display messengers name and disable notifying if already in chat view 2026-03-26 21:31:36 -06:00
2fb409f0d9 add pet and product images 2026-03-26 20:36:04 -06:00
Alex
dbb24085b2 Added push notifications when reciving any message and added filter status on pets in Andriod 2026-03-26 20:13:27 -06:00
Alex
75c39312fe Added profile photo loading and uploading
- profile photos now load from backend
- profile photos can be uploaded to the backend
- RetrofitClient now automatically determines if the device is an emulator or hardware so we dont have to comment and uncomment everytime we test with a different device
2026-03-26 16:50:02 -06:00
Alex
aec9f7b9e0 Added role based access to android login
- Admin has access to everything
- Staff has limited access to what they can edit in listfragment
- Customers cannot login to app
- added validations to pets, supplier and services in their detailed view
2026-03-26 16:50:02 -06:00
5477c4beee use swing picker on wayland 2026-03-25 23:55:40 -06:00
4659aa44df readd secure avatar endpoints 2026-03-25 22:58:04 -06:00
b1fe03410c Merge pull request #30 from RecentRunner/WorkingOnProfileAndPushNotification
Working on profile and push notification
2026-03-25 09:18:48 -06:00
d3fdf4f823 Merge pull request #29 from RecentRunner/desktop---validator-fixes
added null checks to validator, created a bunch of junit tests
2026-03-24 20:43:54 -06:00
augmentedpotato
dbdf5e54ab added null checks to validator, created a bunch of junit tests 2026-03-24 20:42:45 -06:00
b012c91b3b Merge pull request #28 from RecentRunner/web-index
uploading index to repo
2026-03-24 16:59:39 -06:00
601 changed files with 5577 additions and 10360 deletions

245
README.md
View File

@@ -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

View File

@@ -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;

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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 -> {

View File

@@ -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);

View File

@@ -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(); }
}

View File

@@ -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();

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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(); }
}

View File

@@ -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(); }
}

View File

@@ -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();

View File

@@ -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);

View File

@@ -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();

View File

@@ -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(); }
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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,

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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,

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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")

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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());
}
}

View File

@@ -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()

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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")

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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");

View File

@@ -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();

View File

@@ -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")

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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()));

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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")

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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