@propbinder/mobile-design 0.2.47 → 0.2.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ng-package.json +24 -0
- package/package.json +3 -39
- package/src/animations/page-transitions.ts +165 -0
- package/src/assets/fonts/brockmann-mediumitalic-webfont.woff2 +0 -0
- package/src/assets/fonts/brockmann-regularitalic-webfont.woff2 +0 -0
- package/src/assets/fonts/brockmann-semibolditalic-webfont.woff2 +0 -0
- package/src/components/action-list-item/ds-mobile-action-list-item.ts +102 -0
- package/src/components/action-list-item/index.ts +2 -0
- package/src/components/app-icon/ds-app-icon.ts +133 -0
- package/src/components/app-icon/index.ts +2 -0
- package/src/components/attachment-preview/ds-mobile-attachment-preview.css +139 -0
- package/src/components/attachment-preview/ds-mobile-attachment-preview.ts +164 -0
- package/src/components/attachment-preview/index.ts +1 -0
- package/src/components/avatar-with-badge/ds-avatar-with-badge.ts +142 -0
- package/src/components/avatar-with-badge/index.ts +2 -0
- package/src/components/booking-modal/ds-mobile-booking-confirmation-wrapper.ts +71 -0
- package/src/components/booking-modal/ds-mobile-booking-modal.service.ts +121 -0
- package/src/components/booking-modal/ds-mobile-booking-modal.ts +598 -0
- package/src/components/booking-modal/ds-mobile-booking-summary.ts +161 -0
- package/src/components/booking-modal/index.ts +4 -0
- package/src/components/bottom-sheet/ds-mobile-actions-bottom-sheet.ts +266 -0
- package/src/components/bottom-sheet/ds-mobile-bottom-sheet-header.ts +146 -0
- package/src/components/bottom-sheet/ds-mobile-bottom-sheet-wrapper.ts +156 -0
- package/src/components/bottom-sheet/ds-mobile-bottom-sheet.css +101 -0
- package/src/components/bottom-sheet/ds-mobile-bottom-sheet.service.ts +169 -0
- package/src/components/bottom-sheet/ds-mobile-confirmation-sheet.ts +211 -0
- package/src/components/bottom-sheet/ds-mobile-post-create-bottom-sheet.ts +578 -0
- package/src/components/bottom-sheet/ds-mobile-profile-actions-sheet.ts +614 -0
- package/src/components/bottom-sheet/index.ts +8 -0
- package/src/components/bottom-sheet/modal-shadow-fix.ts +42 -0
- package/src/components/card-inline/ds-mobile-card-inline.ts +301 -0
- package/src/components/card-inline/index.ts +2 -0
- package/src/components/card-inline-banner/ds-mobile-card-inline-banner.ts +118 -0
- package/src/components/card-inline-banner/index.ts +1 -0
- package/src/components/card-inline-contact/ds-mobile-card-inline-contact.ts +120 -0
- package/src/components/card-inline-contact/index.ts +1 -0
- package/src/components/card-inline-file/ds-mobile-card-inline-file.ts +141 -0
- package/src/components/card-inline-file/index.ts +1 -0
- package/src/components/chat-modal/ds-mobile-chat-modal.css +159 -0
- package/src/components/chat-modal/ds-mobile-chat-modal.service.ts +105 -0
- package/src/components/chat-modal/ds-mobile-chat-modal.ts +918 -0
- package/src/components/chat-modal/index.ts +8 -0
- package/src/components/comment/ds-mobile-comment.ts +568 -0
- package/src/components/comment/index.ts +2 -0
- package/src/components/contact-list-item/ds-mobile-contact-list-item.ts +182 -0
- package/src/components/contact-list-item/index.ts +2 -0
- package/src/components/content/ds-mobile-content.ts +139 -0
- package/src/components/content/index.ts +2 -0
- package/src/components/dropdown/ds-mobile-dropdown.css +199 -0
- package/src/components/dropdown/ds-mobile-dropdown.ts +340 -0
- package/src/components/dropdown/index.ts +2 -0
- package/src/components/ds-mobile-tabs.css +407 -0
- package/src/components/ds-mobile-tabs.ts +216 -0
- package/src/components/empty-state/ds-mobile-empty-state.ts +120 -0
- package/src/components/empty-state/index.ts +2 -0
- package/src/components/fab/ds-mobile-fab.ts +315 -0
- package/src/components/fab/index.ts +1 -0
- package/src/components/facility-creation-modal/ds-mobile-facility-creation-confirmation-wrapper.ts +121 -0
- package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.css +189 -0
- package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.service.ts +135 -0
- package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.ts +656 -0
- package/src/components/facility-creation-modal/index.ts +9 -0
- package/src/components/facility-creation-modal/sheets/ds-mobile-access-sheet.ts +105 -0
- package/src/components/facility-creation-modal/sheets/ds-mobile-price-sheet.ts +188 -0
- package/src/components/facility-creation-modal/sheets/ds-mobile-when-can-book-sheet.ts +460 -0
- package/src/components/facility-creation-modal/sheets/ds-mobile-who-can-book-sheet.ts +134 -0
- package/src/components/facility-detail-modal/ds-mobile-facility-detail-modal.service.ts +69 -0
- package/src/components/facility-detail-modal/ds-mobile-facility-detail-modal.ts +379 -0
- package/src/components/facility-detail-modal/index.ts +2 -0
- package/src/components/file-attachment/ds-mobile-file-attachment.ts +164 -0
- package/src/components/file-attachment/index.ts +2 -0
- package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.css +214 -0
- package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.service.ts +84 -0
- package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.ts +424 -0
- package/src/components/handbook-detail-modal/index.ts +3 -0
- package/src/components/handbook-folder/ds-mobile-handbook-folder-mini.ts +175 -0
- package/src/components/handbook-folder/ds-mobile-handbook-folder.ts +533 -0
- package/src/components/handbook-folder/index.ts +4 -0
- package/src/components/header-content/ds-mobile-header-content.ts +222 -0
- package/src/components/header-content/index.ts +2 -0
- package/src/components/illustration/ds-mobile-illustration.ts +124 -0
- package/src/components/illustration/index.ts +2 -0
- package/src/components/index.ts +124 -0
- package/src/components/inline-photo/ds-mobile-inline-photo.ts +361 -0
- package/src/components/inline-photo/index.ts +1 -0
- package/src/components/inline-tabs/ds-mobile-inline-tabs.ts +132 -0
- package/src/components/inline-tabs/index.ts +2 -0
- package/src/components/interactive-list-item-booking/ds-mobile-interactive-list-item-booking.ts +350 -0
- package/src/components/interactive-list-item-booking/index.ts +1 -0
- package/src/components/interactive-list-item-inquiry/ds-mobile-interactive-list-item-inquiry.ts +321 -0
- package/src/components/interactive-list-item-inquiry/index.ts +2 -0
- package/src/components/interactive-list-item-message/ds-mobile-interactive-list-item-message.ts +237 -0
- package/src/components/interactive-list-item-message/index.ts +2 -0
- package/src/components/interactive-list-item-post/ds-mobile-interactive-list-item-post.ts +549 -0
- package/src/components/interactive-list-item-post/ds-mobile-post-pdf-attachment.ts +124 -0
- package/src/components/interactive-list-item-post/index.ts +13 -0
- package/src/components/lightbox/ds-mobile-lightbox-footer.ts +315 -0
- package/src/components/lightbox/ds-mobile-lightbox-header.ts +202 -0
- package/src/components/lightbox/ds-mobile-lightbox-image.ts +484 -0
- package/src/components/lightbox/ds-mobile-lightbox-pdf.css +377 -0
- package/src/components/lightbox/ds-mobile-lightbox-pdf.ts +374 -0
- package/src/components/lightbox/ds-mobile-lightbox.css +587 -0
- package/src/components/lightbox/ds-mobile-lightbox.service.ts +296 -0
- package/src/components/lightbox/ds-mobile-lightbox.ts +529 -0
- package/src/components/lightbox/index.ts +22 -0
- package/src/components/list-item/ds-mobile-list-item.ts +603 -0
- package/src/components/list-item/index.ts +2 -0
- package/src/components/list-item-static/ds-mobile-list-item-static.ts +133 -0
- package/src/components/list-item-static/index.ts +2 -0
- package/src/components/loader-overlay/ds-mobile-loader-overlay.css +49 -0
- package/src/components/loader-overlay/ds-mobile-loader-overlay.ts +77 -0
- package/src/components/loader-overlay/index.ts +1 -0
- package/src/components/logo/ds-logo.ts +95 -0
- package/src/components/logo/index.ts +2 -0
- package/src/components/message-bubble/ds-mobile-message-bubble.ts +633 -0
- package/src/components/message-bubble/index.ts +7 -0
- package/src/components/message-composer/ds-mobile-message-composer.ts +1146 -0
- package/src/components/message-composer/index.ts +7 -0
- package/src/components/modal/ds-mobile-modal.css +163 -0
- package/src/components/modal/ds-mobile-modal.service.ts +329 -0
- package/src/components/modal/index.ts +8 -0
- package/src/components/modal-base/ds-mobile-modal-base.css +378 -0
- package/src/components/modal-base/ds-mobile-modal-base.ts +261 -0
- package/src/components/modal-base/index.ts +2 -0
- package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.css +112 -0
- package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.service.ts +93 -0
- package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.ts +442 -0
- package/src/components/new-inquiry-modal/index.ts +4 -0
- package/src/components/offline-banner/ds-mobile-offline-banner.ts +135 -0
- package/src/components/offline-banner/index.ts +1 -0
- package/src/components/page-details/ds-mobile-page-details.css +83 -0
- package/src/components/page-details/ds-mobile-page-details.ts +282 -0
- package/src/components/page-details/index.ts +2 -0
- package/src/components/page-main/ds-mobile-page-main.css +68 -0
- package/src/components/page-main/ds-mobile-page-main.ts +421 -0
- package/src/components/page-main/index.ts +2 -0
- package/src/components/post-composer/ds-mobile-post-composer.ts +140 -0
- package/src/components/post-composer/index.ts +2 -0
- package/src/components/post-detail-modal/ds-mobile-post-detail-modal.css +390 -0
- package/src/components/post-detail-modal/ds-mobile-post-detail-modal.service.ts +108 -0
- package/src/components/post-detail-modal/ds-mobile-post-detail-modal.ts +722 -0
- package/src/components/post-detail-modal/index.ts +9 -0
- package/src/components/property-banner/ds-mobile-property-banner.ts +95 -0
- package/src/components/property-banner/index.ts +2 -0
- package/src/components/section/ds-mobile-section.ts +263 -0
- package/src/components/section/index.ts +2 -0
- package/src/components/shared/directives/index.ts +2 -0
- package/src/components/shared/directives/long-press.directive.ts +212 -0
- package/src/components/shared/index.ts +3 -0
- package/src/components/shared/mobile-modal-base.ts +457 -0
- package/src/components/shared/mobile-page-base.ts +204 -0
- package/src/components/swiper/ds-mobile-swiper-with-nav.ts +160 -0
- package/src/components/swiper/ds-mobile-swiper.ts +327 -0
- package/src/components/swiper/index.ts +3 -0
- package/src/components/system-message-banner/ds-mobile-system-message-banner.ts +129 -0
- package/src/components/system-message-banner/index.ts +2 -0
- package/src/components/tab-bar/ds-mobile-tab-bar.css +533 -0
- package/src/components/tab-bar/ds-mobile-tab-bar.ts +735 -0
- package/src/components/tab-bar/index.ts +2 -0
- package/src/components/tabs/ds-mobile-tabs.css +25 -0
- package/src/components/tabs/ds-mobile-tabs.ts +89 -0
- package/src/components/tabs/index.ts +2 -0
- package/src/components/text-input/ds-text-input.ts +287 -0
- package/src/components/text-input/index.ts +2 -0
- package/src/examples/booking.page.ts +434 -0
- package/src/examples/community.page.ts +776 -0
- package/src/examples/handbook.page.ts +324 -0
- package/src/examples/home.page.ts +347 -0
- package/src/examples/index.ts +12 -0
- package/src/examples/inquiries.example.ts +273 -0
- package/src/examples/inquiry-detail.example.css +189 -0
- package/src/examples/inquiry-detail.example.ts +415 -0
- package/src/examples/mobile-tabs-example.component.ts +208 -0
- package/src/examples/post-create.page.ts +311 -0
- package/src/examples/post-detail.page.ts +296 -0
- package/src/examples/sign-in.page.ts +291 -0
- package/src/examples/whitelabel-demo-modal.component.ts +1094 -0
- package/src/examples/whitelabel-demo-modal.service.ts +77 -0
- package/src/models/index.ts +7 -0
- package/src/models/post.model.ts +41 -0
- package/src/pages/community.page.ts +769 -0
- package/src/pages/handbook.page.ts +388 -0
- package/src/pages/home.page.ts +303 -0
- package/src/pages/index.ts +11 -0
- package/src/pages/inquiries.example.ts +273 -0
- package/src/pages/inquiry-detail.example.css +189 -0
- package/src/pages/inquiry-detail.example.ts +415 -0
- package/src/pages/mobile-tabs-example.component.ts +179 -0
- package/src/pages/post-create.page.ts +311 -0
- package/src/pages/post-detail.page.ts +296 -0
- package/src/pages/sign-in.page.ts +291 -0
- package/src/pages/whitelabel-demo-modal.component.ts +1094 -0
- package/src/pages/whitelabel-demo-modal.service.ts +77 -0
- package/src/public-api.ts +6 -0
- package/src/services/base-modal.service.ts +101 -0
- package/src/services/index.ts +11 -0
- package/src/services/posts.service.ts +542 -0
- package/src/services/tracking-permission.service.ts +88 -0
- package/src/services/user.service.ts +60 -0
- package/src/services/whitelabel.service.ts +675 -0
- package/{styles → src/styles}/ionic.css +25 -0
- package/tsconfig.lib.json +17 -0
- package/tsconfig.lib.prod.json +9 -0
- package/tsconfig.spec.json +13 -0
- package/fesm2022/propbinder-mobile-design.mjs +0 -26136
- package/fesm2022/propbinder-mobile-design.mjs.map +0 -1
- package/index.d.ts +0 -8154
- /package/{assets → src/assets}/fonts/Brockmann-Bold.otf +0 -0
- /package/{assets → src/assets}/fonts/Brockmann-BoldItalic.otf +0 -0
- /package/{assets → src/assets}/fonts/Brockmann-Medium.otf +0 -0
- /package/{assets → src/assets}/fonts/Brockmann-MediumItalic.otf +0 -0
- /package/{assets → src/assets}/fonts/Brockmann-Regular.otf +0 -0
- /package/{assets → src/assets}/fonts/Brockmann-RegularItalic.otf +0 -0
- /package/{assets → src/assets}/fonts/Brockmann-SemiBold.otf +0 -0
- /package/{assets → src/assets}/fonts/Brockmann-SemiBoldItalic.otf +0 -0
- /package/{assets → src/assets}/fonts/Brockmann_desktop_license.pdf +0 -0
- /package/{assets → src/assets}/fonts/brockmann-medium-webfont.woff2 +0 -0
- /package/{assets → src/assets}/fonts/brockmann-regular-webfont.woff2 +0 -0
- /package/{assets → src/assets}/fonts/brockmann-semibold-webfont.woff2 +0 -0
- /package/{styles → src/components/shared}/mobile-common.css +0 -0
- /package/{styles → src/components/shared}/mobile-page-base.css +0 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
NEW INQUIRY MODAL STYLES
|
|
3
|
+
============================================ */
|
|
4
|
+
|
|
5
|
+
/* Large Title Textarea - Matches design system pattern */
|
|
6
|
+
.inquiry-title-input {
|
|
7
|
+
flex: 0 0 auto; /* Don't grow like description */
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.inquiry-title-input ::ng-deep textarea {
|
|
11
|
+
font-size: var(--font-size-2xl);
|
|
12
|
+
font-weight: 400;
|
|
13
|
+
line-height: 1.2;
|
|
14
|
+
overflow-y: hidden;
|
|
15
|
+
resize: none;
|
|
16
|
+
box-sizing: border-box;
|
|
17
|
+
/* Add right padding to prevent overlap with close button */
|
|
18
|
+
padding-right: 52px; /* 36px button + 16px spacing */
|
|
19
|
+
/* Height will be controlled dynamically by JavaScript */
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* ============================================
|
|
23
|
+
FIXED BOTTOM CONTAINER (Slides with keyboard)
|
|
24
|
+
============================================ */
|
|
25
|
+
|
|
26
|
+
.fixed-bottom-container {
|
|
27
|
+
background: var(--color-background-neutral-primary);
|
|
28
|
+
border-top: 1px solid var(--border-color-default);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Attachment Previews Section (Inside Fixed Bottom) */
|
|
32
|
+
.attachment-previews-section {
|
|
33
|
+
padding: 16px 20px;
|
|
34
|
+
border-bottom: 1px solid var(--border-color-default);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.image-previews {
|
|
38
|
+
display: flex;
|
|
39
|
+
gap: 8px;
|
|
40
|
+
overflow-x: auto;
|
|
41
|
+
padding-bottom: 4px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* Hide scrollbar for photo previews */
|
|
45
|
+
.image-previews::-webkit-scrollbar {
|
|
46
|
+
display: none;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Submit Actions Container */
|
|
50
|
+
.submit-container {
|
|
51
|
+
padding: 16px 20px;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.submit-content {
|
|
55
|
+
display: flex;
|
|
56
|
+
align-items: center;
|
|
57
|
+
justify-content: space-between;
|
|
58
|
+
gap: 16px;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* Upload Actions (Left Side) */
|
|
62
|
+
.upload-actions {
|
|
63
|
+
display: flex;
|
|
64
|
+
align-items: center;
|
|
65
|
+
gap: 8px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.upload-actions ds-icon-button::ng-deep button {
|
|
69
|
+
width: 44px;
|
|
70
|
+
height: 44px;
|
|
71
|
+
border-radius: 50%;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Submit Button (Right Side) */
|
|
75
|
+
.submit-content ds-button::ng-deep button {
|
|
76
|
+
border-radius: 100px;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.submit-content ds-button.submit-action-button::ng-deep button {
|
|
80
|
+
height: 44px;
|
|
81
|
+
min-height: 44px;
|
|
82
|
+
max-height: 44px;
|
|
83
|
+
padding-left: 16px;
|
|
84
|
+
padding-right: 16px;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Description Textarea - Expand to fill available space */
|
|
88
|
+
.inquiry-description-input {
|
|
89
|
+
display: flex;
|
|
90
|
+
flex: 1;
|
|
91
|
+
min-height: 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.inquiry-description-input ::ng-deep .textarea-container {
|
|
95
|
+
display: flex;
|
|
96
|
+
flex: 1;
|
|
97
|
+
min-height: 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.inquiry-description-input ::ng-deep textarea {
|
|
101
|
+
flex: 1;
|
|
102
|
+
min-height: 80px;
|
|
103
|
+
overflow-y: hidden;
|
|
104
|
+
resize: none;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* Responsive adjustments */
|
|
108
|
+
@media (min-width: 768px) {
|
|
109
|
+
.submit-container {
|
|
110
|
+
padding: 20px 32px;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { ModalController } from '@ionic/angular/standalone';
|
|
3
|
+
import { DsMobileNewInquiryModalComponent, NewInquiryData } from './ds-mobile-new-inquiry-modal';
|
|
4
|
+
import { BaseModalService } from '../../services/base-modal.service';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Options for opening the new inquiry modal
|
|
8
|
+
*/
|
|
9
|
+
export interface NewInquiryModalOptions {
|
|
10
|
+
/** Callback function when inquiry is submitted */
|
|
11
|
+
onSubmit?: (data: NewInquiryData) => void | Promise<void>;
|
|
12
|
+
/** Initial loading state */
|
|
13
|
+
loading?: boolean;
|
|
14
|
+
/** Initial error message */
|
|
15
|
+
error?: string;
|
|
16
|
+
/** Custom placeholder for title field */
|
|
17
|
+
titlePlaceholder?: string;
|
|
18
|
+
/** Custom placeholder for description field */
|
|
19
|
+
descriptionPlaceholder?: string;
|
|
20
|
+
/** Custom label for submit button */
|
|
21
|
+
submitButtonLabel?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* DsMobileNewInquiryModalService
|
|
26
|
+
*
|
|
27
|
+
* Service for displaying the new inquiry creation modal.
|
|
28
|
+
* Built on Ionic's modal system with native gestures and animations.
|
|
29
|
+
*
|
|
30
|
+
* Features:
|
|
31
|
+
* - Full-screen modal with form
|
|
32
|
+
* - Title and description inputs
|
|
33
|
+
* - Photo upload with camera/gallery
|
|
34
|
+
* - Form validation
|
|
35
|
+
* - Submit handling
|
|
36
|
+
* - Loading and error states
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* constructor(private newInquiryModal: DsMobileNewInquiryModalService) {}
|
|
41
|
+
*
|
|
42
|
+
* async createNewInquiry(): Promise<void> {
|
|
43
|
+
* console.log('[InquiriesPage] FAB clicked, opening modal...');
|
|
44
|
+
*
|
|
45
|
+
* await this.newInquiryModal.open({
|
|
46
|
+
* onSubmit: async (data) => {
|
|
47
|
+
* console.log('Creating inquiry:', data);
|
|
48
|
+
* // Call your API to create the inquiry
|
|
49
|
+
* await this.apiService.createInquiry(data);
|
|
50
|
+
* // Close the modal
|
|
51
|
+
* await this.newInquiryModal.close();
|
|
52
|
+
* }
|
|
53
|
+
* });
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
@Injectable({
|
|
58
|
+
providedIn: 'root',
|
|
59
|
+
})
|
|
60
|
+
export class DsMobileNewInquiryModalService extends BaseModalService {
|
|
61
|
+
constructor(modalController: ModalController) {
|
|
62
|
+
super(modalController);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Open the new inquiry modal
|
|
67
|
+
*
|
|
68
|
+
* @param options Modal options including onSubmit callback
|
|
69
|
+
* @returns Promise that resolves when the modal is presented
|
|
70
|
+
*/
|
|
71
|
+
async open(options?: NewInquiryModalOptions): Promise<void> {
|
|
72
|
+
// console.log('[NewInquiryModal] Opening modal with options:', options);
|
|
73
|
+
|
|
74
|
+
const modal = await this.createModal(
|
|
75
|
+
DsMobileNewInquiryModalComponent,
|
|
76
|
+
{
|
|
77
|
+
onSubmit: options?.onSubmit,
|
|
78
|
+
loading: options?.loading ?? false,
|
|
79
|
+
...(options?.error && { error: options.error }),
|
|
80
|
+
...(options?.titlePlaceholder && { titlePlaceholder: options.titlePlaceholder }),
|
|
81
|
+
...(options?.descriptionPlaceholder && { descriptionPlaceholder: options.descriptionPlaceholder }),
|
|
82
|
+
...(options?.submitButtonLabel && { submitButtonLabel: options.submitButtonLabel }),
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
keyboardClose: false, // Don't close on keyboard hide for this modal
|
|
86
|
+
},
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
console.log('[NewInquiryModal] Modal created, presenting...');
|
|
90
|
+
await modal.present();
|
|
91
|
+
console.log('[NewInquiryModal] Modal presented');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import { Component, signal, Input, ViewChild, ElementRef, OnInit, AfterViewInit, CUSTOM_ELEMENTS_SCHEMA, inject } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { FormsModule } from '@angular/forms';
|
|
4
|
+
import { ModalController } from '@ionic/angular/standalone';
|
|
5
|
+
import { Capacitor } from '@capacitor/core';
|
|
6
|
+
import { FilePicker } from '@capawesome/capacitor-file-picker';
|
|
7
|
+
import { DsButtonComponent, DsTextareaComponent, DsIconButtonComponent } from '@propbinder/design-system';
|
|
8
|
+
import { DsMobileModalBaseComponent } from '../modal-base/ds-mobile-modal-base';
|
|
9
|
+
import { DsMobileAttachmentPreviewComponent, type AttachmentData, type AttachmentFileType } from '../attachment-preview';
|
|
10
|
+
import { DsMobileSectionComponent } from '../section';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Photo interface for inquiry (deprecated - use AttachmentData)
|
|
14
|
+
* @deprecated Use AttachmentData from attachment-preview component instead
|
|
15
|
+
*/
|
|
16
|
+
export interface InquiryPhoto {
|
|
17
|
+
id: string;
|
|
18
|
+
src: string;
|
|
19
|
+
alt?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* New inquiry form data
|
|
24
|
+
*/
|
|
25
|
+
export interface NewInquiryData {
|
|
26
|
+
title: string;
|
|
27
|
+
description: string;
|
|
28
|
+
attachments: AttachmentData[];
|
|
29
|
+
category?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* DsMobileNewInquiryModalComponent
|
|
34
|
+
*
|
|
35
|
+
* Modal component for creating new inquiries.
|
|
36
|
+
* Uses ds-mobile-modal-base for consistent layout and behavior.
|
|
37
|
+
*
|
|
38
|
+
* Features:
|
|
39
|
+
* - Title and description fields
|
|
40
|
+
* - Photo upload with preview
|
|
41
|
+
* - Submit button at bottom
|
|
42
|
+
* - Form validation
|
|
43
|
+
* - Camera/photo picker integration
|
|
44
|
+
*
|
|
45
|
+
* This component is typically not used directly - use DsMobileNewInquiryModalService instead.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* // Don't instantiate directly - use the service:
|
|
50
|
+
* constructor(private inquiryModal: DsMobileNewInquiryModalService) {}
|
|
51
|
+
*
|
|
52
|
+
* createInquiry() {
|
|
53
|
+
* this.inquiryModal.open({
|
|
54
|
+
* onSubmit: (data) => console.log('Inquiry created:', data)
|
|
55
|
+
* });
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
@Component({
|
|
60
|
+
selector: 'ds-mobile-new-inquiry-modal',
|
|
61
|
+
standalone: true,
|
|
62
|
+
imports: [
|
|
63
|
+
CommonModule,
|
|
64
|
+
FormsModule,
|
|
65
|
+
DsButtonComponent,
|
|
66
|
+
DsTextareaComponent,
|
|
67
|
+
DsIconButtonComponent,
|
|
68
|
+
DsMobileModalBaseComponent,
|
|
69
|
+
DsMobileAttachmentPreviewComponent,
|
|
70
|
+
DsMobileSectionComponent,
|
|
71
|
+
],
|
|
72
|
+
styleUrls: ['../shared/mobile-common.css', './ds-mobile-new-inquiry-modal.css'],
|
|
73
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
74
|
+
template: `
|
|
75
|
+
<ds-mobile-modal-base [loading]="loading" [error]="error" [showHeader]="false" [hasFixedBottom]="true" [enableKeyboardHandling]="true" [keyboardContentBehavior]="'overlay'" closeButtonLabel="Close">
|
|
76
|
+
<!-- Form Content -->
|
|
77
|
+
<ds-mobile-section>
|
|
78
|
+
<!-- Title Field (Large Ghost Textarea) -->
|
|
79
|
+
<ds-textarea
|
|
80
|
+
#titleInput
|
|
81
|
+
[(ngModel)]="title"
|
|
82
|
+
[ghost]="true"
|
|
83
|
+
[required]="true"
|
|
84
|
+
[rows]="1"
|
|
85
|
+
[placeholder]="titlePlaceholder"
|
|
86
|
+
class="inquiry-title-input ghost-input-clean"
|
|
87
|
+
(valueChange)="handleTitleChange($event)"
|
|
88
|
+
/>
|
|
89
|
+
|
|
90
|
+
<!-- Description Field (Ghost Textarea) -->
|
|
91
|
+
<ds-textarea
|
|
92
|
+
#descriptionInput
|
|
93
|
+
[(ngModel)]="description"
|
|
94
|
+
[ghost]="true"
|
|
95
|
+
[rows]="1"
|
|
96
|
+
[placeholder]="descriptionPlaceholder"
|
|
97
|
+
class="inquiry-description-input ghost-input-clean"
|
|
98
|
+
(valueChange)="handleDescriptionChange($event)"
|
|
99
|
+
/>
|
|
100
|
+
</ds-mobile-section>
|
|
101
|
+
|
|
102
|
+
<!-- Fixed Bottom Container (Slides with keyboard) -->
|
|
103
|
+
<div footer class="fixed-bottom-container">
|
|
104
|
+
<!-- Attachment Previews (if any) -->
|
|
105
|
+
@if (attachments().length > 0) {
|
|
106
|
+
<div class="attachment-previews-section">
|
|
107
|
+
<div class="image-previews">
|
|
108
|
+
@for (attachment of attachments(); track attachment.id) {
|
|
109
|
+
<ds-mobile-attachment-preview [attachment]="attachment" (remove)="removeAttachment($event)" />
|
|
110
|
+
}
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
<!-- Submit Actions Container -->
|
|
116
|
+
<div class="submit-container">
|
|
117
|
+
<div class="submit-content">
|
|
118
|
+
<!-- Upload Actions (Left) -->
|
|
119
|
+
<div class="upload-actions">
|
|
120
|
+
<ds-icon-button icon="remixImageLine" variant="secondary" size="md" (clicked)="addPhoto()" [disabled]="attachments().length >= 6" aria-label="Add photo">
|
|
121
|
+
</ds-icon-button>
|
|
122
|
+
<ds-icon-button
|
|
123
|
+
icon="remixAttachmentLine"
|
|
124
|
+
variant="secondary"
|
|
125
|
+
size="md"
|
|
126
|
+
(clicked)="handleAddAttachment()"
|
|
127
|
+
[disabled]="attachments().length >= 6"
|
|
128
|
+
aria-label="Add attachment"
|
|
129
|
+
>
|
|
130
|
+
</ds-icon-button>
|
|
131
|
+
|
|
132
|
+
<!-- Hidden file input for file selection -->
|
|
133
|
+
<input #fileInput type="file" accept="*/*" multiple (change)="handleFileSelect($event)" style="display: none;" aria-hidden="true" />
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<!-- Submit Button (Right) -->
|
|
137
|
+
<ds-button class="submit-action-button" variant="primary" size="md" [disabled]="!isFormValid() || isSubmitting()" (clicked)="handleSubmit()">
|
|
138
|
+
{{ submitButtonLabel }}
|
|
139
|
+
</ds-button>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</ds-mobile-modal-base>
|
|
144
|
+
`,
|
|
145
|
+
})
|
|
146
|
+
export class DsMobileNewInquiryModalComponent implements OnInit, AfterViewInit {
|
|
147
|
+
private modalController = inject(ModalController);
|
|
148
|
+
@ViewChild('titleInput', { read: ElementRef }) titleInputRef?: ElementRef<HTMLElement>;
|
|
149
|
+
@ViewChild('descriptionInput', { read: ElementRef }) descriptionInputRef?: ElementRef<HTMLElement>;
|
|
150
|
+
@ViewChild('titleInput') titleInput?: DsTextareaComponent;
|
|
151
|
+
@ViewChild('fileInput') fileInput?: ElementRef<HTMLInputElement>;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Loading state for the modal
|
|
155
|
+
*/
|
|
156
|
+
@Input() loading: boolean = false;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Error message to display
|
|
160
|
+
*/
|
|
161
|
+
@Input() error?: string;
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Callback function when form is submitted
|
|
165
|
+
*/
|
|
166
|
+
@Input() onSubmit?: (data: NewInquiryData) => void | Promise<void>;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Placeholder for the title field
|
|
170
|
+
*/
|
|
171
|
+
@Input() titlePlaceholder: string = 'Name your inquiry';
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Placeholder for the description field
|
|
175
|
+
*/
|
|
176
|
+
@Input() descriptionPlaceholder: string = 'Tell us what this inquiry is about...';
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Label for the submit button
|
|
180
|
+
*/
|
|
181
|
+
@Input() submitButtonLabel: string = 'Submit';
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Form title field
|
|
185
|
+
*/
|
|
186
|
+
title = '';
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Form description field
|
|
190
|
+
*/
|
|
191
|
+
description = '';
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Attachments array (replaces photos)
|
|
195
|
+
*/
|
|
196
|
+
attachments = signal<AttachmentData[]>([]);
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Form validation state
|
|
200
|
+
*/
|
|
201
|
+
isFormValid = signal<boolean>(false);
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Submitting state
|
|
205
|
+
*/
|
|
206
|
+
isSubmitting = signal<boolean>(false);
|
|
207
|
+
|
|
208
|
+
ngOnInit(): void {
|
|
209
|
+
console.log('[NewInquiryModal] Component initialized');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
ngAfterViewInit(): void {
|
|
213
|
+
// Setup auto-resize for title textarea
|
|
214
|
+
setTimeout(() => {
|
|
215
|
+
this.autoResizeTitleTextarea();
|
|
216
|
+
this.autoResizeDescriptionTextarea();
|
|
217
|
+
|
|
218
|
+
// Focus the title textarea after view initialization to trigger keyboard on iOS
|
|
219
|
+
if (this.titleInputRef) {
|
|
220
|
+
const textareaElement = this.titleInputRef.nativeElement.querySelector('textarea');
|
|
221
|
+
if (textareaElement) {
|
|
222
|
+
textareaElement.focus();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}, 300);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Auto-resize the title textarea based on content
|
|
230
|
+
*/
|
|
231
|
+
private autoResizeTitleTextarea(): void {
|
|
232
|
+
if (!this.titleInputRef) return;
|
|
233
|
+
|
|
234
|
+
// Access the native textarea element
|
|
235
|
+
const textareaElement = this.titleInputRef.nativeElement.querySelector('textarea');
|
|
236
|
+
if (textareaElement) {
|
|
237
|
+
textareaElement.style.height = 'auto';
|
|
238
|
+
textareaElement.style.height = textareaElement.scrollHeight + 'px';
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Auto-resize the description textarea based on content
|
|
244
|
+
*/
|
|
245
|
+
private autoResizeDescriptionTextarea(): void {
|
|
246
|
+
if (!this.descriptionInputRef) return;
|
|
247
|
+
|
|
248
|
+
const textareaElement = this.descriptionInputRef.nativeElement.querySelector('textarea');
|
|
249
|
+
if (textareaElement) {
|
|
250
|
+
textareaElement.style.height = 'auto';
|
|
251
|
+
textareaElement.style.height = textareaElement.scrollHeight + 'px';
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Handle title change with auto-resize
|
|
257
|
+
*/
|
|
258
|
+
handleTitleChange(value: string): void {
|
|
259
|
+
this.validateForm();
|
|
260
|
+
this.autoResizeTitleTextarea();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Handle description change with auto-resize
|
|
265
|
+
*/
|
|
266
|
+
handleDescriptionChange(value: string): void {
|
|
267
|
+
this.validateForm();
|
|
268
|
+
this.autoResizeDescriptionTextarea();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Validate form fields
|
|
273
|
+
*/
|
|
274
|
+
validateForm(): void {
|
|
275
|
+
const isValid = this.title.trim().length > 0 && this.description.trim().length > 0;
|
|
276
|
+
this.isFormValid.set(isValid);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Add a new photo from camera/library
|
|
281
|
+
*/
|
|
282
|
+
async addPhoto(): Promise<void> {
|
|
283
|
+
if (this.attachments().length >= 6) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
try {
|
|
288
|
+
const result = await FilePicker.pickImages({
|
|
289
|
+
limit: 1,
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
const image = result.files?.[0];
|
|
293
|
+
if (image) {
|
|
294
|
+
const newAttachment: AttachmentData = {
|
|
295
|
+
id: `photo-${Date.now()}`,
|
|
296
|
+
src: image.path ? Capacitor.convertFileSrc(image.path) : (image.blob ? URL.createObjectURL(image.blob) : ''),
|
|
297
|
+
type: 'image',
|
|
298
|
+
name: image.name || `Photo ${this.attachments().length + 1}`,
|
|
299
|
+
size: this.formatFileSize(image.size ?? 0),
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
this.attachments.update((attachments) => [...attachments, newAttachment]);
|
|
303
|
+
}
|
|
304
|
+
} catch (error) {
|
|
305
|
+
console.error('[NewInquiryModal] Error adding photo:', error);
|
|
306
|
+
// User cancelled or error occurred - just ignore
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Remove an attachment
|
|
312
|
+
*/
|
|
313
|
+
removeAttachment(attachmentId: string): void {
|
|
314
|
+
this.attachments.update((attachments) => attachments.filter((a) => a.id !== attachmentId));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Handle attachment button click
|
|
319
|
+
*/
|
|
320
|
+
handleAddAttachment(): void {
|
|
321
|
+
if (this.attachments().length >= 6) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Trigger the hidden file input
|
|
326
|
+
if (this.fileInput) {
|
|
327
|
+
this.fileInput.nativeElement.click();
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Detect file type from file name or mime type
|
|
333
|
+
*/
|
|
334
|
+
private detectFileType(file: File): AttachmentFileType {
|
|
335
|
+
const fileName = file.name.toLowerCase();
|
|
336
|
+
const mimeType = file.type.toLowerCase();
|
|
337
|
+
|
|
338
|
+
// Check if it's an image
|
|
339
|
+
if (mimeType.startsWith('image/')) {
|
|
340
|
+
return 'image';
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Check file extension
|
|
344
|
+
if (fileName.endsWith('.pdf')) {
|
|
345
|
+
return 'pdf';
|
|
346
|
+
} else if (fileName.endsWith('.doc')) {
|
|
347
|
+
return 'doc';
|
|
348
|
+
} else if (fileName.endsWith('.docx')) {
|
|
349
|
+
return 'docx';
|
|
350
|
+
} else if (fileName.endsWith('.xls')) {
|
|
351
|
+
return 'xls';
|
|
352
|
+
} else if (fileName.endsWith('.xlsx')) {
|
|
353
|
+
return 'xlsx';
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return 'other';
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Format file size for display
|
|
361
|
+
*/
|
|
362
|
+
private formatFileSize(bytes: number): string {
|
|
363
|
+
if (bytes === 0) return '0 B';
|
|
364
|
+
const k = 1024;
|
|
365
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
366
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
367
|
+
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Handle file selection from file input
|
|
372
|
+
*/
|
|
373
|
+
handleFileSelect(event: Event): void {
|
|
374
|
+
const input = event.target as HTMLInputElement;
|
|
375
|
+
const files = input.files;
|
|
376
|
+
|
|
377
|
+
if (!files || files.length === 0) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Process each selected file (up to the limit)
|
|
382
|
+
const remainingSlots = 6 - this.attachments().length;
|
|
383
|
+
const filesToProcess = Array.from(files).slice(0, remainingSlots);
|
|
384
|
+
|
|
385
|
+
filesToProcess.forEach((file) => {
|
|
386
|
+
const fileType = this.detectFileType(file);
|
|
387
|
+
|
|
388
|
+
// Create a data URL for preview
|
|
389
|
+
const reader = new FileReader();
|
|
390
|
+
reader.onload = (e) => {
|
|
391
|
+
const result = e.target?.result as string;
|
|
392
|
+
if (result) {
|
|
393
|
+
const newAttachment: AttachmentData = {
|
|
394
|
+
id: `file-${Date.now()}-${Math.random()}`,
|
|
395
|
+
src: result,
|
|
396
|
+
type: fileType,
|
|
397
|
+
name: file.name,
|
|
398
|
+
size: this.formatFileSize(file.size),
|
|
399
|
+
};
|
|
400
|
+
this.attachments.update((attachments) => [...attachments, newAttachment]);
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
reader.readAsDataURL(file);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
// Reset the input so the same file can be selected again
|
|
407
|
+
input.value = '';
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Handle form submission
|
|
412
|
+
*/
|
|
413
|
+
async handleSubmit(): Promise<void> {
|
|
414
|
+
if (!this.isFormValid() || this.isSubmitting()) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
this.isSubmitting.set(true);
|
|
419
|
+
|
|
420
|
+
try {
|
|
421
|
+
const inquiryData: NewInquiryData = {
|
|
422
|
+
title: this.title.trim(),
|
|
423
|
+
description: this.description.trim(),
|
|
424
|
+
attachments: this.attachments(),
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
console.log('[NewInquiryModal] Submitting inquiry:', inquiryData);
|
|
428
|
+
|
|
429
|
+
if (this.onSubmit) {
|
|
430
|
+
await this.onSubmit(inquiryData);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Success - close the modal
|
|
434
|
+
this.modalController.dismiss();
|
|
435
|
+
} catch (error) {
|
|
436
|
+
console.error('[NewInquiryModal] Error submitting inquiry:', error);
|
|
437
|
+
this.error = 'Failed to create inquiry. Please try again.';
|
|
438
|
+
} finally {
|
|
439
|
+
this.isSubmitting.set(false);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { DsMobileNewInquiryModalComponent } from './ds-mobile-new-inquiry-modal';
|
|
2
|
+
export type { NewInquiryData, InquiryPhoto } from './ds-mobile-new-inquiry-modal';
|
|
3
|
+
export { DsMobileNewInquiryModalService } from './ds-mobile-new-inquiry-modal.service';
|
|
4
|
+
export type { NewInquiryModalOptions } from './ds-mobile-new-inquiry-modal.service';
|