From 76840a8e1bba2504b9cd4085d0b578c40684f96b Mon Sep 17 00:00:00 2001 From: louiscklaw Date: Thu, 19 Jun 2025 23:06:51 +0800 Subject: [PATCH] feat: add comprehensive ServiceMenu and MainTabs components with hotel service listings, QR code access, and user profile settings integration --- 03_source/mobile/.prettierrc | 6 + 03_source/mobile/src/App.tsx | 6 +- 03_source/mobile/src/PATHS.ts | 2 + .../src/components/CloseButton/close.svg | 5 + .../src/components/CloseButton/index.tsx | 46 ++++ .../CarousellMe/ListingsContent/index.tsx | 17 +- .../mobile/src/pages/CarousellMe/index.tsx | 66 ++++-- .../mobile/src/pages/CarousellMe/style.scss | 8 +- .../pages/CarousellMe/theme/variables.scss | 59 +++++ 03_source/mobile/src/pages/MainTabs.tsx | 98 -------- .../mobile/src/pages/MainTabs/MenuButton.tsx | 34 +++ .../mobile/src/pages/MainTabs/ServiceMenu.tsx | 135 +++++++++++ 03_source/mobile/src/pages/MainTabs/index.tsx | 187 ++++++++++++++++ .../mobile/src/pages/MainTabs/style.scss | 59 +++++ .../pages/tabs/carousell_me/QRPage/index.tsx | 210 ++++++++++++++++++ .../pages/tabs/carousell_me/QRPage/style.scss | 2 + .../tabs/carousell_me/settings/index.tsx | 161 ++++++++++++++ .../tabs/carousell_me/settings/style.scss | 2 + .../carousell_me/settings/svgs/coupon.svg | 3 + .../tabs/carousell_me/settings/svgs/data.svg | 3 + .../tabs/carousell_me/settings/svgs/index.tsx | 33 +++ .../carousell_me/settings/svgs/notebook.svg | 3 + .../carousell_me/settings/svgs/printer.svg | 3 + .../tabs/carousell_me/settings/svgs/rice.svg | 3 + .../carousell_me/settings/svgs/suitcase.svg | 3 + .../tabs/carousell_me/settings/svgs/truck.svg | 3 + .../tabs/carousell_me/settings/svgs/voice.svg | 3 + 03_source/mobile/src/theme/variables.css | 36 ++- 28 files changed, 1053 insertions(+), 143 deletions(-) create mode 100644 03_source/mobile/src/components/CloseButton/close.svg create mode 100644 03_source/mobile/src/components/CloseButton/index.tsx create mode 100644 03_source/mobile/src/pages/CarousellMe/theme/variables.scss delete mode 100644 03_source/mobile/src/pages/MainTabs.tsx create mode 100644 03_source/mobile/src/pages/MainTabs/MenuButton.tsx create mode 100644 03_source/mobile/src/pages/MainTabs/ServiceMenu.tsx create mode 100644 03_source/mobile/src/pages/MainTabs/index.tsx create mode 100644 03_source/mobile/src/pages/MainTabs/style.scss create mode 100644 03_source/mobile/src/pages/tabs/carousell_me/QRPage/index.tsx create mode 100644 03_source/mobile/src/pages/tabs/carousell_me/QRPage/style.scss create mode 100644 03_source/mobile/src/pages/tabs/carousell_me/settings/index.tsx create mode 100644 03_source/mobile/src/pages/tabs/carousell_me/settings/style.scss create mode 100644 03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/coupon.svg create mode 100644 03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/data.svg create mode 100644 03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/index.tsx create mode 100644 03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/notebook.svg create mode 100644 03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/printer.svg create mode 100644 03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/rice.svg create mode 100644 03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/suitcase.svg create mode 100644 03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/truck.svg create mode 100644 03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/voice.svg diff --git a/03_source/mobile/.prettierrc b/03_source/mobile/.prettierrc index 924bec9..b220dba 100644 --- a/03_source/mobile/.prettierrc +++ b/03_source/mobile/.prettierrc @@ -10,6 +10,12 @@ "jsonRecursiveSort": false, "jsonSortOrder": "{\"*\": \"lexical\"}", "overrides": [ + { + "files": "src/pages/MainTabs/index.tsx", + "options": { + "printWidth": 240 + } + }, { "files": "src/App.tsx", "options": { diff --git a/03_source/mobile/src/App.tsx b/03_source/mobile/src/App.tsx index 4956ab0..611b0db 100644 --- a/03_source/mobile/src/App.tsx +++ b/03_source/mobile/src/App.tsx @@ -70,7 +70,7 @@ import DummyPayPage from './pages/DummyEventPayPage'; import DummyEventPayPage from './pages/DummyEventPayPage'; import PaymentSuccess from './pages/PaymentSuccess'; import PaymentFailed from './pages/PaymentFailed'; -import CarousellMe from './pages/CarousellMe'; +import settings from './pages/tabs/carousell_me/settings'; setupIonicReact(); @@ -159,9 +159,9 @@ const IonicApp: React.FC = ({ darkMode, schedule, setIsLoggedIn, /> {/* PartyUser */} - - + + diff --git a/03_source/mobile/src/PATHS.ts b/03_source/mobile/src/PATHS.ts index 53bdb53..9a1316e 100644 --- a/03_source/mobile/src/PATHS.ts +++ b/03_source/mobile/src/PATHS.ts @@ -105,5 +105,7 @@ const PATHS = { // CAROUSELL_ME: '/tabs/carousell_me', + CAROUSELL_ME_QR: '/tabs/carousell_me/qr_page', + CAROUSELL_ME_SETTINGS: '/tabs/carousell_me/settings', }; export default PATHS; diff --git a/03_source/mobile/src/components/CloseButton/close.svg b/03_source/mobile/src/components/CloseButton/close.svg new file mode 100644 index 0000000..f169000 --- /dev/null +++ b/03_source/mobile/src/components/CloseButton/close.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/03_source/mobile/src/components/CloseButton/index.tsx b/03_source/mobile/src/components/CloseButton/index.tsx new file mode 100644 index 0000000..18dbdee --- /dev/null +++ b/03_source/mobile/src/components/CloseButton/index.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { IonList, IonItem, IonLabel, IonText } from '@ionic/react'; + +import closeSvg from './close.svg'; + +interface CloseButtonProps { + onClick: (e: React.UIEvent) => void; +} + +const CloseButton: React.FC = ({ onClick }) => { + return ( + <> +
+
+ +
Close
+
+
+ + ); +}; + +export default React.memo(CloseButton); diff --git a/03_source/mobile/src/pages/CarousellMe/ListingsContent/index.tsx b/03_source/mobile/src/pages/CarousellMe/ListingsContent/index.tsx index 133631d..e24a013 100644 --- a/03_source/mobile/src/pages/CarousellMe/ListingsContent/index.tsx +++ b/03_source/mobile/src/pages/CarousellMe/ListingsContent/index.tsx @@ -63,10 +63,18 @@ import { format, parseISO } from 'date-fns'; // import lookForVisitorSvg from './look_for_visitor.svg'; const SellingButton: React.FC = () => { + const router = useIonRouter(); return ( <>
- console.log('helloworld')}> + { + window.location.href = + 'https://www.carousell.com.hk/p/coding-javascript-python-vba-freelance-1192889425/'; + }} + > Silhouette of mountains = ({ hide }) => { display: 'flex', flexDirection: 'row', flexWrap: 'wrap', - justifyContent: 'space-evenly', + justifyContent: 'flex-start', }} > - - - - -
diff --git a/03_source/mobile/src/pages/CarousellMe/index.tsx b/03_source/mobile/src/pages/CarousellMe/index.tsx index aa04a46..fe80bd5 100644 --- a/03_source/mobile/src/pages/CarousellMe/index.tsx +++ b/03_source/mobile/src/pages/CarousellMe/index.tsx @@ -34,7 +34,9 @@ import { IonSegmentButton, useIonRouter, } from '@ionic/react'; + import './style.scss'; + import { locationSharp, settingsOutline, @@ -56,6 +58,9 @@ import { heartSharp, heartOutline, chatbubblesOutline, + heart, + chatbubbles, + chatbox, } from 'ionicons/icons'; import AboutPopover from '../../components/AboutPopover'; import { format, parseISO } from 'date-fns'; @@ -67,8 +72,13 @@ import ReviewsContent from './ReviewsContent'; import ListingsContent from './ListingsContent'; import { triggerShare } from '../../util/triggerShare'; import { connect } from '../../data/connect'; +import PATHS from '../../PATHS'; // import { toProperCase } from 'src/util/toProperCase'; +import './theme/variables.scss'; +import { messages } from '../DemoFacebookClone/main/messages'; +import { Router } from 'react-router'; + interface OwnProps {} interface StateProps { @@ -133,16 +143,43 @@ const CarousellMe: React.FC = () => { - +
+ +
- - 1 +
+ + +
+
99
+
+
- + {/* */} + {/* */} + {/* */}
{/* Instead of loading an image each time the select changes, use opacity to transition them */}
= () => { }} >
-
= () => { />
- + = () => { > - + route.push(PATHS.CAROUSELL_ME_SETTINGS, 'forward', 'push')} // onClick={handleSettingPageClick} > = () => {
-
-
Louis Law
+
+
louis_coding
= () => { gap: '0.8rem', }} > - @louiscklaw + @louis_coding
= () => {
- {/* listing / reviews / about */} = () => { {t('About')} {/* listing / reviews / about */} - diff --git a/03_source/mobile/src/pages/CarousellMe/style.scss b/03_source/mobile/src/pages/CarousellMe/style.scss index 7e659d9..6666abd 100644 --- a/03_source/mobile/src/pages/CarousellMe/style.scss +++ b/03_source/mobile/src/pages/CarousellMe/style.scss @@ -1,7 +1,13 @@ #carousell-me-page { + --ion-color-tertiary: #ad8e70; + --ion-color-tertiary-rgb: 173, 142, 112; + --ion-color-tertiary-contrast: #000000; + --ion-color-tertiary-contrast-rgb: 0, 0, 0; + --ion-color-tertiary-shade: #987d63; + --ion-color-tertiary-tint: #b5997e; + .setting-icons { ion-button::part(native) { - // https://ionicframework.com/docs/theming/css-shadow-parts margin: 0; padding: 0; } diff --git a/03_source/mobile/src/pages/CarousellMe/theme/variables.scss b/03_source/mobile/src/pages/CarousellMe/theme/variables.scss new file mode 100644 index 0000000..b42a90e --- /dev/null +++ b/03_source/mobile/src/pages/CarousellMe/theme/variables.scss @@ -0,0 +1,59 @@ +#carousell-me-page { + ion-button { + --ion-color-primary: #243763; + --ion-color-primary-rgb: 36, 55, 99; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #203057; + --ion-color-primary-tint: #3a4b73; + + --ion-color-secondary: #ff6e31; + --ion-color-secondary-rgb: 255, 110, 49; + --ion-color-secondary-contrast: #000000; + --ion-color-secondary-contrast-rgb: 0, 0, 0; + --ion-color-secondary-shade: #e0612b; + --ion-color-secondary-tint: #ff7d46; + + --ion-color-tertiary: #ad8e70; + --ion-color-tertiary-rgb: 173, 142, 112; + --ion-color-tertiary-contrast: #000000; + --ion-color-tertiary-contrast-rgb: 0, 0, 0; + --ion-color-tertiary-shade: #987d63; + --ion-color-tertiary-tint: #b5997e; + + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #000000; + --ion-color-success-contrast-rgb: 0, 0, 0; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #000000; + --ion-color-medium-contrast-rgb: 0, 0, 0; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + --ion-color-light: #ffebb7; + --ion-color-light-rgb: 255, 235, 183; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #e0cfa1; + --ion-color-light-tint: #ffedbe; + } +} diff --git a/03_source/mobile/src/pages/MainTabs.tsx b/03_source/mobile/src/pages/MainTabs.tsx deleted file mode 100644 index cc19962..0000000 --- a/03_source/mobile/src/pages/MainTabs.tsx +++ /dev/null @@ -1,98 +0,0 @@ -// REQ0116/main-tab - -import React from 'react'; -import { IonTabs, IonRouterOutlet, IonTabBar, IonTabButton, IonIcon, IonLabel } from '@ionic/react'; -import { Route, Redirect } from 'react-router'; -import { calendar, location, informationCircle, people } from 'ionicons/icons'; -import SchedulePage from './SchedulePage'; -import SpeakerList from './SpeakerList'; -import SpeakerDetail from './SpeakerDetail'; -import SessionDetail from './SessionDetail'; -import MapView from './MapView'; -import About from './About'; -import EventList from './EventList'; -import MembersNearByList from './MembersNearByList'; -import OrderList from './OrderList'; -import MyProfile from './MyProfile'; -import MessageList from './MessageList'; -import PATHS from '../PATHS'; -import Favourites from './Favourites'; -import Helloworld from './Helloworld'; -import TabAppRoute from '../TabAppRoute'; -import CarousellMe from './CarousellMe'; - -interface MainTabsProps {} - -const MainTabs: React.FC = () => { - return ( - - - {/* REQ0117/default-route */} - - {/* - Using the render method prop cuts down the number of renders your components will have due to route changes. - Use the component prop when your component depends on the RouterComponentProps passed in automatically. - */} - } exact={true} /> - } exact={true} /> - - - - - } exact={true} /> - - } exact={true} /> - } exact={true} /> - - {/* */} - } exact={true} /> - {/* - - - - - - */} - - {/* */} - - - {/* */} - - {/* - - - Speakers - - */} - - - - Event - - - - Nearby - - {/* - - - Order - - */} - - - - - Message - - - - Profile - - - - ); -}; - -export default MainTabs; diff --git a/03_source/mobile/src/pages/MainTabs/MenuButton.tsx b/03_source/mobile/src/pages/MainTabs/MenuButton.tsx new file mode 100644 index 0000000..1189efc --- /dev/null +++ b/03_source/mobile/src/pages/MainTabs/MenuButton.tsx @@ -0,0 +1,34 @@ +import { IonCard, IonImg } from '@ionic/react'; +import React from 'react'; + +interface MenuButtonProps { + iconUrl: string; + buttonText: string; + targetUrl: string; + onClick: (e: React.UIEvent) => void; +} + +const MenuButton: React.FC = ({ iconUrl, buttonText, targetUrl, onClick }) => { + return ( + <> + + +
{buttonText}
+
(附加費)
+
+ + ); +}; + +export default React.memo(MenuButton); diff --git a/03_source/mobile/src/pages/MainTabs/ServiceMenu.tsx b/03_source/mobile/src/pages/MainTabs/ServiceMenu.tsx new file mode 100644 index 0000000..75e2646 --- /dev/null +++ b/03_source/mobile/src/pages/MainTabs/ServiceMenu.tsx @@ -0,0 +1,135 @@ +import { + IonButton, + IonButtons, + IonContent, + IonGrid, + IonHeader, + IonItem, + IonModal, + IonRow, + IonTitle, + IonToolbar, + useIonModal, +} from '@ionic/react'; +import React, { useState } from 'react'; +import CloseButton from '../../components/CloseButton'; +import MenuButton from './MenuButton'; +import { useTranslation } from 'react-i18next'; + +import './style.scss'; + +interface ServiceMenuProps { + hotelServiceMenu: any; + showCenterButton: boolean; + buttonSvg: any; +} + +// bookmark: facilities, bottom drawer, ServiceMenu +const ServiceMenu: React.FC = ({ + buttonSvg, + showCenterButton, + hotelServiceMenu, +}) => { + const { t } = useTranslation(); + + const [drawerOpen, setDrawerOpen] = useState(false); + + function dismiss() { + setDrawerOpen(false); + } + + return ( + <> + +
+
+
{t('Greetings from xxx hotel')}
+

{t('please enjoy our services')}

+
+
+ {hotelServiceMenu.map((service: any, index: number) => ( + console.log('archive ?')} + targetUrl={service.targetUrl} + /> + ))} +
+ + + + dismiss()} /> + + + +
+
+ + {/* */} +
+
setDrawerOpen(true)} + >
+
+ + ); +}; + +export default React.memo(ServiceMenu); diff --git a/03_source/mobile/src/pages/MainTabs/index.tsx b/03_source/mobile/src/pages/MainTabs/index.tsx new file mode 100644 index 0000000..d940508 --- /dev/null +++ b/03_source/mobile/src/pages/MainTabs/index.tsx @@ -0,0 +1,187 @@ +// REQ0116/main-tab +// +import React, { useEffect, useState } from 'react'; +import { IonTabs, IonRouterOutlet, IonTabBar, IonTabButton, IonIcon, IonLabel, IonButton } from '@ionic/react'; +import { Route, Redirect } from 'react-router'; +import { calendar, location, informationCircle, people } from 'ionicons/icons'; +import SchedulePage from '../SchedulePage'; +import SpeakerList from '../SpeakerList'; +import SpeakerDetail from '../SpeakerDetail'; +import SessionDetail from '../SessionDetail'; +import MapView from '../MapView'; +import About from '../About'; +import EventList from '../EventList'; +import MembersNearByList from '../MembersNearByList'; +import OrderList from '../OrderList'; +import MyProfile from '../MyProfile'; +import MessageList from '../MessageList'; +import PATHS from '../../PATHS'; +import Favourites from '../Favourites'; +import Helloworld from '../Helloworld'; +import TabAppRoute from '../../TabAppRoute'; +import CarousellMe from '../CarousellMe'; +import ServiceMenu from './ServiceMenu'; + +// +import QRPage from '../tabs/carousell_me/QRPage'; + +// +import svgIcon1 from './icons/air-conditioning-cooling-heat-temperature-svgrepo-com.svg'; +import svgIcon2 from './icons/alert-attention-caution-dangerous-warning-svgrepo-com.svg'; +import svgIcon3 from './icons/alert-bell-call-message-sign-svgrepo-com.svg'; +import svgIcon4 from './icons/assistance-business-person-reception-service-svgrepo-com.svg'; +import svgIcon5 from './icons/bag-baggage-luggage-suitcase-travel-svgrepo-com.svg'; +import svgIcon6 from './icons/baggage-bellhop-hotel-service-waiter-svgrepo-com.svg'; +import svgIcon7 from './icons/bag-holiday-journey-suitcase-vacation-svgrepo-com.svg'; +import svgIcon8 from './icons/banking-financial-money-payment-transaction-svgrepo-com.svg'; +import svgIcon9 from './icons/bar-cafe-chair-club-stool-svgrepo-com.svg'; +import svgIcon10 from './icons/bath-bathroom-cotton-textile-towel-svgrepo-com.svg'; +import svgIcon11 from './icons/bathroom-bathtub-bubble-foam-water-svgrepo-com.svg'; +import svgIcon12 from './icons/bathroom-faucet-room-sink-wash-svgrepo-com.svg'; +import svgIcon13 from './icons/bed-furniture-interior-pillow-rest-2-svgrepo-com.svg'; +import svgIcon14 from './icons/bed-furniture-interior-pillow-rest-3-svgrepo-com.svg'; +import svgIcon15 from './icons/bed-furniture-interior-pillow-rest-svgrepo-com.svg'; +import svgIcon16 from './icons/book-catalog-document-guidebook-instruction-svgrepo-com.svg'; +import svgIcon17 from './icons/business-finance-money-saving-wallet-svgrepo-com.svg'; +import svgIcon18 from './icons/cafe-card-food-menu-vintage-svgrepo-com.svg'; +import svgIcon19 from './icons/cafe-cup-drink-mug-tea-svgrepo-com.svg'; +import svgIcon20 from './icons/calendar-date-day-month-time-svgrepo-com.svg'; +import svgIcon21 from './icons/cappuccino-coffee-cup-drink-espresso-2-svgrepo-com.svg'; +import svgIcon22 from './icons/cappuccino-coffee-cup-drink-espresso-svgrepo-com.svg'; +import svgIcon23 from './icons/card-credit-currency-finance-money-svgrepo-com.svg'; +import svgIcon24 from './icons/card-door-key-lock-security-svgrepo-com.svg'; +import svgIcon25 from './icons/chair-comfortable-decoration-garden-terrace-svgrepo-com.svg'; +import svgIcon26 from './icons/clean-clothing-laundry-washing-wind-svgrepo-com.svg'; +import svgIcon27 from './icons/comfortable-fabric-footwear-shoe-slipper-svgrepo-com.svg'; +import svgIcon28 from './icons/coupon-entertainment-event-paper-ticket-svgrepo-com.svg'; +import svgIcon29 from './icons/door-elevator-entrance-floor-lift-svgrepo-com.svg'; +import svgIcon30 from './icons/door-enter-entry-exit-open-svgrepo-com.svg'; +import svgIcon31 from './icons/door-entrance-handle-key-security-svgrepo-com.svg'; +import svgIcon32 from './icons/door-key-lock-room-security-svgrepo-com.svg'; +import svgIcon33 from './icons/electronic-internet-screen-technology-television-svgrepo-com.svg'; +import svgIcon34 from './icons/holiday-hotel-journey-service-travel-2-svgrepo-com.svg'; +import svgIcon35 from './icons/holiday-hotel-journey-service-travel-svgrepo-com.svg'; +import svgIcon36 from './icons/holiday-hotel-motel-sign-travel-svgrepo-com.svg'; +import svgIcon37 from './icons/holiday-journey-luggage-suitcase-vacation-2-svgrepo-com.svg'; +import svgIcon38 from './icons/hotel-location-map-pin-travel-svgrepo-com.svg'; +import getButtonSvg from '../../api/getButtonSvg'; +// + +const hotelServiceMenu = [ + { name: '停車場', icon: svgIcon1, targetUrl: '/tabs/hotel_service_intro' }, + { name: '接機服務', icon: svgIcon2, targetUrl: '/tabs/hotel_service_intro' }, + { name: '餐廳', icon: svgIcon3, targetUrl: '/tabs/hotel/:hotelid/restaurants' }, + { name: '咖啡室', icon: svgIcon4, targetUrl: '/tabs/restaurant/:restaurant_id/food/:food_id/intro' }, + { name: '酒吧', icon: svgIcon5, targetUrl: '/tabs/hotel_service_intro' }, + { name: '會議室', icon: svgIcon6, targetUrl: '/tabs/hotel_service_intro' }, + { name: '托兒服務', icon: svgIcon7, targetUrl: '/tabs/hotel_service_intro' }, + { name: '茶室', icon: svgIcon8, targetUrl: '/tabs/hotel_service_intro' }, + { name: '多功能室', icon: svgIcon9, targetUrl: '/tabs/hotel_service_intro' }, + { name: '商務便利設施', icon: svgIcon10, targetUrl: '/tabs/hotel_service_intro' }, + { name: '健身室', icon: svgIcon11, targetUrl: '/tabs/hotel_service_intro' }, + { name: '公共區域 Wi-Fi免費', icon: svgIcon12, targetUrl: '/tabs/hotel_service_intro' }, + { name: '貨幣兌換', icon: svgIcon13, targetUrl: '/tabs/hotel_service_intro' }, + { name: '行李寄存免費', icon: svgIcon14, targetUrl: '/tabs/hotel_service_intro' }, + { name: '叫醒服務', icon: svgIcon16, targetUrl: '/tabs/hotel_service_intro' }, + { name: '關於酒店', icon: svgIcon16, targetUrl: '/tabs/hotel_intro' }, +]; + +interface MainTabsProps {} + +const MainTabs: React.FC = () => { + let [buttonSvg, setButtonSvg] = useState(null); + + let [mainTabVisible, setMainTabVisible] = useState<'bottom' | undefined>(undefined); + let [showCenterButton, setShowCenterButton] = useState(mainTabVisible == 'bottom'); + + const hideMainTab = () => { + setMainTabVisible(undefined); + setShowCenterButton(false); + }; + const showMainTab = () => { + setMainTabVisible('bottom'); + setShowCenterButton(true); + }; + + useEffect(() => { + getButtonSvg().then((res) => setButtonSvg(res)); + }, []); + + return ( +
+ + + {/* */} + + + {/* REQ0117/default-route */} + + + {/* + Using the render method prop cuts down the number of renders your components will have due to route changes. + Use the component prop when your component depends on the RouterComponentProps passed in automatically. + */} + + } exact={true} /> + } exact={true} /> + + + + + } exact={true} /> + + } exact={true} /> + } exact={true} /> + + {/* */} + } exact={true} /> + + {/* + + + + + */} + + + + + + {/* + + + Speakers + + */} + + + + Event + + + + Nearby + + {/* + + + Order + + */} + + + + + Message + + + + Profile + + + +
+ ); +}; + +export default MainTabs; diff --git a/03_source/mobile/src/pages/MainTabs/style.scss b/03_source/mobile/src/pages/MainTabs/style.scss new file mode 100644 index 0000000..a51c64a --- /dev/null +++ b/03_source/mobile/src/pages/MainTabs/style.scss @@ -0,0 +1,59 @@ +// TODO: Shadow Parts, https://ionicframework.com/docs/theming/css-shadow-parts +ion-modal#main-tabs-modal { + // --width: fit-content; + --min-width: 250px; + --height: fit-content; + + --border-radius: 1rem 1rem 0 0; + --box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4); + + // display: flex; + // flex-direction: column; + // justify-content: flex-end; +} + +ion-modal#main-tabs-modal h1 { + margin: 20px 20px 10px 20px; +} + +ion-modal#main-tabs-modal ion-icon { + margin-right: 6px; + + width: 48px; + height: 48px; + + padding: 4px 0; + + color: #aaaaaa; +} + +ion-modal#main-tabs-modal .wrapper { + margin-bottom: 10px; +} + +.colcenter { + display: flex; + flex-direction: row; + justify-content: center; +} + +ion-item::part(native):hover { + color: green; +} + +.service-button { + box-shadow: none; + margin: 0; + + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + gap: 0.5rem; + + ion-img { + min-width: 80%; + height: 40px; + padding-bottom: 3px; + } +} diff --git a/03_source/mobile/src/pages/tabs/carousell_me/QRPage/index.tsx b/03_source/mobile/src/pages/tabs/carousell_me/QRPage/index.tsx new file mode 100644 index 0000000..8388ea3 --- /dev/null +++ b/03_source/mobile/src/pages/tabs/carousell_me/QRPage/index.tsx @@ -0,0 +1,210 @@ +import { + IonBackButton, + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonPopover, + IonSegment, + IonSegmentButton, + IonText, + IonTitle, + IonToolbar, + useIonRouter, + useIonViewDidEnter, +} from '@ionic/react'; +import { chevronBackOutline, star, starOutline } from 'ionicons/icons'; +import React, { useContext, useEffect, useRef, useState } from 'react'; +// import AboutPopover from '../../components/AboutPopover'; +import { useIonAlert } from '@ionic/react'; + +import './style.scss'; + +import { AppContext } from '../../../../data/AppContext'; +import { createGesture, Gesture } from '@ionic/react'; +import { useTranslation } from 'react-i18next'; +import QRCode from 'react-qr-code'; + +interface QRCodeProps {} + +type SessionListProps = { + hide: boolean; +}; + +const MyCode: React.FC = ({ hide }) => { + if (hide) return <>; + return ( + <> + +
+ +
+
+ Scan to follow me on Carousell! +
+
+ + ); +}; + +const ScanCode: React.FC = ({ hide }) => { + if (hide) return <>; + return ( + <> + Scan code + + ); +}; + +const QRPage: React.FC = () => { + const [showPopover, setShowPopover] = useState(false); + const [popoverEvent, setPopoverEvent] = useState(); + const [location, setLocation] = useState<'madison' | 'austin' | 'chicago' | 'seattle'>('madison'); + const [conferenceDate, setConferenceDate] = useState('2047-05-17T00:00:00-05:00'); + const [showAlert, hideAlert] = useIonAlert(); + + const { browser_store } = useContext(AppContext); + + const refRectangle = useRef(null); + const [swipeType, setSwipeType] = useState(); + const [deltaX, setDeltaX] = useState(); + const [velocityX, setVelocityX] = useState(); + const [swipeVerdict, setSwipeVerdict] = useState('helloworld'); + + const route = useIonRouter(); + + useEffect(() => { + if (swipeVerdict == 'swipe-left') + if (route.canGoBack() == true) { + route.goBack(); + } else { + route.push('/tabs/schedule'); + } + }, [swipeVerdict]); + + const onMove = (detail: any) => { + const type = detail.type; + const currentX = detail.currentX; + const deltaX = detail.deltaX; + const velocityX = detail.velocityX; + setSwipeType(type); + setDeltaX(deltaX); + setVelocityX(velocityX); + + if (type == 'pan') + if (Math.abs(deltaX) > 50) + if (velocityX > 0) { + setSwipeVerdict('swipe-right'); + } else { + setSwipeVerdict('swipe-left'); + } + }; + + useIonViewDidEnter(() => { + let gesture: any = {}; + if (refRectangle?.current) { + gesture = createGesture({ + gestureName: 'helloworld', + el: refRectangle.current, + onMove: (detail) => { + onMove(detail); + }, + }); + + gesture.enable(); + } + + return () => { + if (gesture) { + // gesture?.destroy(); + } + }; + }, [refRectangle]); + + const handleStoreWrite = () => { + (async () => { + await browser_store.set('hello', 'world'); + })(); + }; + + const handleStoreRead = () => { + (async () => { + console.log(await browser_store.get('hello')); + })(); + }; + + const handleStoreClear = () => { + (async () => { + await browser_store.clear(); + })(); + }; + + const handleStoreRemove = () => { + (async () => { + await browser_store.remove('hello'); + })(); + }; + + const handleStoreKeys = () => { + (async () => { + console.dir(await browser_store.keys()); + })(); + }; + + const handleStoreLength = () => { + (async () => { + console.dir(await browser_store.length()); + })(); + }; + + const userInput = 'javascript:alert("Oh no!")'; + const [segment, setSegment] = useState<'my_code' | 'scan_code'>('my_code'); + const { t } = useTranslation(); + + const router = useIonRouter(); + + return ( + + + + + router.goBack()}> + + + + + + + + setSegment(e.detail.value as any)} + > + {t('My Code')} + {t('Scan Code')} + + + + + + + ); +}; + +export default React.memo(QRPage); diff --git a/03_source/mobile/src/pages/tabs/carousell_me/QRPage/style.scss b/03_source/mobile/src/pages/tabs/carousell_me/QRPage/style.scss new file mode 100644 index 0000000..ab80ad0 --- /dev/null +++ b/03_source/mobile/src/pages/tabs/carousell_me/QRPage/style.scss @@ -0,0 +1,2 @@ +#qr-page { +} diff --git a/03_source/mobile/src/pages/tabs/carousell_me/settings/index.tsx b/03_source/mobile/src/pages/tabs/carousell_me/settings/index.tsx new file mode 100644 index 0000000..4065596 --- /dev/null +++ b/03_source/mobile/src/pages/tabs/carousell_me/settings/index.tsx @@ -0,0 +1,161 @@ +import { + IonBackButton, + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonItem, + IonLabel, + IonList, + IonPage, + IonPopover, + IonTitle, + IonToolbar, + useIonRouter, + useIonViewDidEnter, +} from '@ionic/react'; +import { chevronBackOutline, star, starOutline } from 'ionicons/icons'; +import React, { useContext, useEffect, useRef, useState } from 'react'; +// import AboutPopover from '../../components/AboutPopover'; +import { useIonAlert } from '@ionic/react'; + +import './style.scss'; + +import { + ImgCouponSvg, + ImgDataSvg, + ImgNotebookSvg, + ImgPrinterSvg, + ImgRiceSvg, + ImgSuitcaseSvg, + ImgTruckSvg, + ImgVoiceSvg, +} from './svgs'; + +interface SettingProps {} + +const Settings: React.FC = () => { + const [showPopover, setShowPopover] = useState(false); + const [popoverEvent, setPopoverEvent] = useState(); + const [location, setLocation] = useState<'madison' | 'austin' | 'chicago' | 'seattle'>('madison'); + const [conferenceDate, setConferenceDate] = useState('2047-05-17T00:00:00-05:00'); + const [showAlert, hideAlert] = useIonAlert(); + + // const { browser_store } = useContext(AppContext); + + const refRectangle = useRef(null); + const [swipeType, setSwipeType] = useState(); + const [deltaX, setDeltaX] = useState(); + const [velocityX, setVelocityX] = useState(); + const [swipeVerdict, setSwipeVerdict] = useState('helloworld'); + + const route = useIonRouter(); + + return ( + + + + + route.goBack()}> + + + + + + {}}> + {false ? ( + + ) : ( + + )} + + + + + + + + Settings + + + + + + + Edit Profile + + + + Quick and Auto reply + + + {/* help and support */} + +

Help & Support

+
+ + + Support Inbox + + + + Help Centen + + + + Contact Us + + + {/* Theme */} + +

Theme

+
+ + + Dark Mode + + + {/* Settings & Privacy */} + +

Settings & Privacy

+
+ + + Change Password + + + + Notificatinos + + + + Auto-reserve items + + + + Save Photos + + + + Social Media + + + + About Carousell + + + + Logout + + + + Deactivate Account + +
+
+
+ ); +}; + +export default React.memo(Settings); diff --git a/03_source/mobile/src/pages/tabs/carousell_me/settings/style.scss b/03_source/mobile/src/pages/tabs/carousell_me/settings/style.scss new file mode 100644 index 0000000..3a5e059 --- /dev/null +++ b/03_source/mobile/src/pages/tabs/carousell_me/settings/style.scss @@ -0,0 +1,2 @@ +#sample-blank-page { +} diff --git a/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/coupon.svg b/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/coupon.svg new file mode 100644 index 0000000..47f8370 --- /dev/null +++ b/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/coupon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/data.svg b/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/data.svg new file mode 100644 index 0000000..e5bb813 --- /dev/null +++ b/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/data.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/index.tsx b/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/index.tsx new file mode 100644 index 0000000..29a2c68 --- /dev/null +++ b/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/index.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import CouponSvg from './coupon.svg'; +import DataSvg from './data.svg'; +import NotebookSvg from './notebook.svg'; +import PrinterSvg from './printer.svg'; +import RiceSvg from './rice.svg'; +import SuitcaseSvg from './suitcase.svg'; +import TruckSvg from './truck.svg'; +import VoiceSvg from './voice.svg'; + +const ImgSvg = ({ svg }: { svg: string }) => { + return ; +}; + +const ImgCouponSvg = () => ; +const ImgDataSvg = () => ; +const ImgNotebookSvg = () => ; +const ImgPrinterSvg = () => ; +const ImgRiceSvg = () => ; +const ImgSuitcaseSvg = () => ; +const ImgTruckSvg = () => ; +const ImgVoiceSvg = () => ; + +export { + ImgCouponSvg, + ImgDataSvg, + ImgNotebookSvg, + ImgPrinterSvg, + ImgRiceSvg, + ImgSuitcaseSvg, + ImgTruckSvg, + ImgVoiceSvg, +}; diff --git a/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/notebook.svg b/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/notebook.svg new file mode 100644 index 0000000..8cf932d --- /dev/null +++ b/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/notebook.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/printer.svg b/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/printer.svg new file mode 100644 index 0000000..81aeea4 --- /dev/null +++ b/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/printer.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/rice.svg b/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/rice.svg new file mode 100644 index 0000000..2104055 --- /dev/null +++ b/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/rice.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/suitcase.svg b/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/suitcase.svg new file mode 100644 index 0000000..c293b57 --- /dev/null +++ b/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/suitcase.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/truck.svg b/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/truck.svg new file mode 100644 index 0000000..472fd36 --- /dev/null +++ b/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/truck.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/voice.svg b/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/voice.svg new file mode 100644 index 0000000..c80f836 --- /dev/null +++ b/03_source/mobile/src/pages/tabs/carousell_me/settings/svgs/voice.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/03_source/mobile/src/theme/variables.css b/03_source/mobile/src/theme/variables.css index 48d1f13..c94d1a8 100644 --- a/03_source/mobile/src/theme/variables.css +++ b/03_source/mobile/src/theme/variables.css @@ -18,39 +18,39 @@ * https://ionicframework.com/docs/theming/colors#new-color-creator */ - :root { +:root { --ion-color-favorite: #69bb7b; - --ion-color-favorite-rgb: 105,187,123; + --ion-color-favorite-rgb: 105, 187, 123; --ion-color-favorite-contrast: #ffffff; - --ion-color-favorite-contrast-rgb: 255,255,255; + --ion-color-favorite-contrast-rgb: 255, 255, 255; --ion-color-favorite-shade: #5ca56c; --ion-color-favorite-tint: #78c288; --ion-color-twitter: #1da1f4; - --ion-color-twitter-rgb: 29,161,244; + --ion-color-twitter-rgb: 29, 161, 244; --ion-color-twitter-contrast: #ffffff; - --ion-color-twitter-contrast-rgb: 255,255,255; + --ion-color-twitter-contrast-rgb: 255, 255, 255; --ion-color-twitter-shade: #1a8ed7; --ion-color-twitter-tint: #34aaf5; --ion-color-instagram: #5956d8; - --ion-color-instagram-rgb: 89,86,216; + --ion-color-instagram-rgb: 89, 86, 216; --ion-color-instagram-contrast: #ffffff; - --ion-color-instagram-contrast-rgb: 255,255,255; + --ion-color-instagram-contrast-rgb: 255, 255, 255; --ion-color-instagram-shade: #4e4cbe; --ion-color-instagram-tint: #6a67dc; --ion-color-vimeo: #23b6ea; - --ion-color-vimeo-rgb: 35,182,234; + --ion-color-vimeo-rgb: 35, 182, 234; --ion-color-vimeo-contrast: #ffffff; - --ion-color-vimeo-contrast-rgb: 255,255,255; + --ion-color-vimeo-contrast-rgb: 255, 255, 255; --ion-color-vimeo-shade: #1fa0ce; --ion-color-vimeo-tint: #39bdec; --ion-color-facebook: #3b5998; - --ion-color-facebook-rgb: 59,89,152; + --ion-color-facebook-rgb: 59, 89, 152; --ion-color-facebook-contrast: #ffffff; - --ion-color-facebook-contrast-rgb: 255,255,255; + --ion-color-facebook-contrast-rgb: 255, 255, 255; --ion-color-facebook-shade: #344e86; --ion-color-facebook-tint: #4f6aa2; } @@ -110,19 +110,19 @@ } .ion-color-github { - --ion-color-base: #211F1F; - --ion-color-base-rgb: 33,31,31; + --ion-color-base: #211f1f; + --ion-color-base-rgb: 33, 31, 31; --ion-color-contrast: #ffffff; - --ion-color-contrast-rgb: 255,255,255; + --ion-color-contrast-rgb: 255, 255, 255; --ion-color-shade: #1d1b1b; --ion-color-tint: #373535; } .ion-color-instagram { - --ion-color-base: #9537BC; - --ion-color-base-rgb: 149,55,188; + --ion-color-base: #9537bc; + --ion-color-base-rgb: 149, 55, 188; --ion-color-contrast: #ffffff; - --ion-color-contrast-rgb: 255,255,255; + --ion-color-contrast-rgb: 255, 255, 255; --ion-color-shade: #8330a5; --ion-color-tint: #a04bc3; } @@ -157,7 +157,6 @@ */ .ios { - } /* @@ -167,5 +166,4 @@ */ .md { - }