feat: add CarousellMe feature with profile, listings and reviews components
This commit is contained in:
20
03_source/mobile/package-lock.json
generated
20
03_source/mobile/package-lock.json
generated
@@ -62,6 +62,7 @@
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"@types/react-router": "^5.1.20",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/react-star-ratings": "^2.3.3",
|
||||
"@vitejs/plugin-react": "^3.1.0",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^13.2.0",
|
||||
@@ -1326,6 +1327,16 @@
|
||||
"@types/react-router": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-star-ratings": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-star-ratings/-/react-star-ratings-2.3.3.tgz",
|
||||
"integrity": "sha512-8vLqJG1uRA2SmYBBMPWpv6QWHLvZ/a3XmELCIf4xh4VFXT7QkkrcthiLSMjQ4ibDiUtYYpyLB0JoR1lBHSHA2A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/slice-ansi": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/slice-ansi/-/slice-ansi-4.0.0.tgz",
|
||||
@@ -7054,6 +7065,15 @@
|
||||
"@types/react-router": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-star-ratings": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-star-ratings/-/react-star-ratings-2.3.3.tgz",
|
||||
"integrity": "sha512-8vLqJG1uRA2SmYBBMPWpv6QWHLvZ/a3XmELCIf4xh4VFXT7QkkrcthiLSMjQ4ibDiUtYYpyLB0JoR1lBHSHA2A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/slice-ansi": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/slice-ansi/-/slice-ansi-4.0.0.tgz",
|
||||
|
@@ -61,6 +61,7 @@
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"@types/react-router": "^5.1.20",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/react-star-ratings": "^2.3.3",
|
||||
"@vitejs/plugin-react": "^3.1.0",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^13.2.0",
|
||||
|
@@ -70,6 +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';
|
||||
|
||||
setupIonicReact();
|
||||
|
||||
|
@@ -23,11 +23,11 @@ const PATHS = {
|
||||
getOrderDetail: (id: string) => `/order_detail/${id}`,
|
||||
// Tab navigation routes
|
||||
TAB_NOT_IMPLEMENTED: '/tabs/not_implemented',
|
||||
EVENT_LIST: `/tabs/events`,
|
||||
MESSAGE_LIST: `/tabs/messages`,
|
||||
EVENT_LIST: '/tabs/events',
|
||||
MESSAGE_LIST: '/tabs/messages',
|
||||
NEARBY_LIST: '/tabs/nearby',
|
||||
ORDERS_LIST: '/tabs/orders',
|
||||
FAVOURITES_LIST: `/tabs/favourites`,
|
||||
FAVOURITES_LIST: '/tabs/favourites',
|
||||
PROFILE: '/tabs/my_profile',
|
||||
|
||||
// partyUser
|
||||
@@ -102,5 +102,8 @@ const PATHS = {
|
||||
DEMO_SLIDING_PROFILE: '/demo-sliding-profile',
|
||||
DEMO_STICKY_BOTTOM_SHEET_EXAMPLE: '/demo-sticky-bottom-sheet-example',
|
||||
DEMO_STORAGE_EXAMPLE: '/demo-storage-example',
|
||||
|
||||
//
|
||||
CAROUSELL_ME: '/tabs/carousell_me',
|
||||
};
|
||||
export default PATHS;
|
||||
|
@@ -1,14 +1,23 @@
|
||||
import React, { createContext, PropsWithChildren, useReducer } from 'react';
|
||||
import { initialState, AppState, reducers } from './state';
|
||||
|
||||
import { Storage, Drivers } from '@ionic/storage';
|
||||
|
||||
const browser_store = new Storage({
|
||||
name: '__mydb',
|
||||
driverOrder: [Drivers.LocalStorage, Drivers.IndexedDB],
|
||||
});
|
||||
|
||||
export interface AppContextState {
|
||||
state: AppState;
|
||||
dispatch: React.Dispatch<any>;
|
||||
browser_store: any;
|
||||
}
|
||||
|
||||
export const AppContext = createContext<AppContextState>({
|
||||
state: initialState,
|
||||
dispatch: () => undefined,
|
||||
browser_store: {},
|
||||
});
|
||||
|
||||
export const AppContextProvider: React.FC<PropsWithChildren> = ({ children }) => {
|
||||
@@ -19,9 +28,13 @@ export const AppContextProvider: React.FC<PropsWithChildren> = ({ children }) =>
|
||||
value={{
|
||||
state: store,
|
||||
dispatch,
|
||||
//
|
||||
browser_store,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AppContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
//
|
||||
|
130
03_source/mobile/src/pages/CarousellMe/AboutContent.tsx
Normal file
130
03_source/mobile/src/pages/CarousellMe/AboutContent.tsx
Normal file
@@ -0,0 +1,130 @@
|
||||
import React, { useState } from 'react';
|
||||
// import { useTranslation } from 'react-i18next';
|
||||
// import StarRatings from 'react-star-ratings';
|
||||
|
||||
import {
|
||||
IonHeader,
|
||||
IonTitle,
|
||||
IonToolbar,
|
||||
IonContent,
|
||||
IonPage,
|
||||
IonButtons,
|
||||
IonBadge,
|
||||
IonMenuButton,
|
||||
IonButton,
|
||||
IonIcon,
|
||||
IonDatetime,
|
||||
IonSelectOption,
|
||||
IonList,
|
||||
IonItem,
|
||||
IonLabel,
|
||||
IonSelect,
|
||||
IonPopover,
|
||||
IonText,
|
||||
IonCard,
|
||||
IonCardHeader,
|
||||
IonCardTitle,
|
||||
IonCardSubtitle,
|
||||
IonCardContent,
|
||||
IonTabs,
|
||||
IonTabBar,
|
||||
IonTabButton,
|
||||
IonSegment,
|
||||
IonSegmentButton,
|
||||
useIonRouter,
|
||||
} from '@ionic/react';
|
||||
import './style.scss';
|
||||
import {
|
||||
locationSharp,
|
||||
settingsOutline,
|
||||
qrCode,
|
||||
shareSocialOutline,
|
||||
ellipsisHorizontal,
|
||||
ellipsisVertical,
|
||||
logoFacebook,
|
||||
phonePortraitSharp,
|
||||
pricetagSharp,
|
||||
archiveSharp,
|
||||
calendar,
|
||||
personCircle,
|
||||
map,
|
||||
informationCircle,
|
||||
mailSharp,
|
||||
chevronDownSharp,
|
||||
chevronForwardSharp,
|
||||
} from 'ionicons/icons';
|
||||
// import AboutPopover from '../../components/AboutPopover';
|
||||
import { format, parseISO } from 'date-fns';
|
||||
|
||||
import lookForVisitorSvg from './look_for_visitor.svg';
|
||||
|
||||
type SessionListProps = {
|
||||
hide: boolean;
|
||||
};
|
||||
|
||||
const AboutContent: React.FC<SessionListProps> = ({ hide }) => {
|
||||
let route = useIonRouter();
|
||||
|
||||
if (hide) return <></>;
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
//
|
||||
paddingTop: '1rem',
|
||||
paddingLeft: '1rem',
|
||||
paddingRight: '1rem',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '1rem',
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<IonText color="tertiary" style={{ fontSize: '0.8rem' }}>
|
||||
<IonText style={{ fontWeight: 'bold' }} color="success">
|
||||
25
|
||||
</IonText>
|
||||
<IonText>Followers</IonText>
|
||||
{' '}
|
||||
<IonText style={{ fontWeight: 'bold' }} color="success">
|
||||
0
|
||||
</IonText>
|
||||
<IonText>Followers</IonText>
|
||||
</IonText>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
paddingBottom: '3rem',
|
||||
}}
|
||||
>
|
||||
<IonText style={{ fontSize: '0.9rem' }}>
|
||||
<p>接受 payme / 支付寳 / FPS:</p>
|
||||
<p>Accepting HSBC payme / alipay / FPS </p>
|
||||
<p>更多資料 / for more detail:</p>
|
||||
<p>https://louiscklaw.github.io/carousell </p>
|
||||
<p>歡迎PM查詢</p>
|
||||
</IonText>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* private information */}
|
||||
<IonList>
|
||||
<IonItem lines="none">
|
||||
<h3>Private information</h3>
|
||||
</IonItem>
|
||||
<IonItem
|
||||
lines="full"
|
||||
button
|
||||
onClick={(e) => {
|
||||
route.push('/tabs/carousell_me/OffersMade');
|
||||
}}
|
||||
>
|
||||
<IonText style={{ fontSize: '0.9rem' }}>My Offers</IonText>
|
||||
</IonItem>
|
||||
</IonList>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AboutContent;
|
147
03_source/mobile/src/pages/CarousellMe/ListingsContent/index.tsx
Normal file
147
03_source/mobile/src/pages/CarousellMe/ListingsContent/index.tsx
Normal file
@@ -0,0 +1,147 @@
|
||||
import React, { useState } from 'react';
|
||||
// import { useTranslation } from 'react-i18next';
|
||||
// import StarRatings from 'react-star-ratings';
|
||||
|
||||
import {
|
||||
IonHeader,
|
||||
IonTitle,
|
||||
IonToolbar,
|
||||
IonContent,
|
||||
IonPage,
|
||||
IonButtons,
|
||||
IonBadge,
|
||||
IonMenuButton,
|
||||
IonButton,
|
||||
IonIcon,
|
||||
IonDatetime,
|
||||
IonSelectOption,
|
||||
IonList,
|
||||
IonItem,
|
||||
IonLabel,
|
||||
IonSelect,
|
||||
IonPopover,
|
||||
IonText,
|
||||
IonCard,
|
||||
IonCardHeader,
|
||||
IonCardTitle,
|
||||
IonCardSubtitle,
|
||||
IonCardContent,
|
||||
IonTabs,
|
||||
IonTabBar,
|
||||
IonTabButton,
|
||||
IonSegment,
|
||||
IonSegmentButton,
|
||||
useIonRouter,
|
||||
IonSearchbar,
|
||||
IonRow,
|
||||
IonCol,
|
||||
} from '@ionic/react';
|
||||
import './style.scss';
|
||||
import {
|
||||
locationSharp,
|
||||
settingsOutline,
|
||||
qrCode,
|
||||
shareSocialOutline,
|
||||
ellipsisHorizontal,
|
||||
ellipsisVertical,
|
||||
logoFacebook,
|
||||
phonePortraitSharp,
|
||||
pricetagSharp,
|
||||
archiveSharp,
|
||||
calendar,
|
||||
personCircle,
|
||||
map,
|
||||
informationCircle,
|
||||
mailSharp,
|
||||
chevronDownSharp,
|
||||
chevronForwardSharp,
|
||||
filterSharp,
|
||||
} from 'ionicons/icons';
|
||||
// import AboutPopover from '../../components/AboutPopover';
|
||||
import { format, parseISO } from 'date-fns';
|
||||
|
||||
// import lookForVisitorSvg from './look_for_visitor.svg';
|
||||
|
||||
const SellingButton: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<div style={{ width: '50%' }}>
|
||||
<IonCard style={{ margin: '0.5rem' }} button onClick={(e) => console.log('helloworld')}>
|
||||
<img
|
||||
alt="Silhouette of mountains"
|
||||
src="https://ionicframework.com/docs/img/demos/card-media.png"
|
||||
/>
|
||||
<IonCardHeader style={{ padding: '0.5rem' }}>
|
||||
<IonCardTitle style={{ fontSize: '0.8rem' }}>
|
||||
{'#html #css #開發 #指導 #代做 #電子平台 #programming #coding #javascript #python #react #debug'
|
||||
.split('')
|
||||
.slice(0, 20)}
|
||||
</IonCardTitle>
|
||||
<IonCardSubtitle>Card Subtitle</IonCardSubtitle>
|
||||
</IonCardHeader>
|
||||
|
||||
<IonCardContent>View Insights</IonCardContent>
|
||||
</IonCard>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
type SessionListProps = {
|
||||
hide: boolean;
|
||||
};
|
||||
|
||||
const ListingsContent: React.FC<SessionListProps> = ({ hide }) => {
|
||||
let route = useIonRouter();
|
||||
let [openExplain, setOpenExplain] = useState(false);
|
||||
|
||||
if (hide) return <></>;
|
||||
return (
|
||||
<>
|
||||
<div id="listing-content">
|
||||
<div style={{ marginTop: '0.5rem', paddingLeft: '0.5rem', paddingRight: '0.5rem' }}>
|
||||
<IonText style={{ fontWeight: 'bold' }}>33 Listings</IonText>
|
||||
<IonSearchbar
|
||||
mode={'ios'}
|
||||
placeholder="Search the seller's listings"
|
||||
style={{ padding: 0 }}
|
||||
></IonSearchbar>
|
||||
|
||||
<div style={{ fontSize: '0.8rem' }}>
|
||||
<IonButton size="small" fill="outline" onClick={(e) => setOpenExplain(true)}>
|
||||
<IonIcon slot="end" icon={filterSharp} style={{ fontSize: '0.8rem' }}></IonIcon>
|
||||
Filters
|
||||
</IonButton>
|
||||
<IonButton size="small" fill="outline">
|
||||
<IonText>{`Status: ${'All'}`}</IonText>
|
||||
</IonButton>
|
||||
<IonButton size="small" fill="outline">
|
||||
<IonText>{`In: All Categories`}</IonText>
|
||||
</IonButton>
|
||||
</div>
|
||||
</div>
|
||||
{/* Sell listing */}
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-evenly',
|
||||
}}
|
||||
>
|
||||
<SellingButton />
|
||||
<SellingButton />
|
||||
<SellingButton />
|
||||
<SellingButton />
|
||||
<SellingButton />
|
||||
<SellingButton />
|
||||
<SellingButton />
|
||||
<SellingButton />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListingsContent;
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.4 KiB |
@@ -0,0 +1,8 @@
|
||||
#listing-content {
|
||||
ion-button {
|
||||
--padding-start: 5px;
|
||||
--padding-end: 5px;
|
||||
--padding-top: 5px;
|
||||
--padding-bottom: 5px;
|
||||
}
|
||||
}
|
201
03_source/mobile/src/pages/CarousellMe/MyProfile/index.tsx
Normal file
201
03_source/mobile/src/pages/CarousellMe/MyProfile/index.tsx
Normal file
@@ -0,0 +1,201 @@
|
||||
import {
|
||||
IonBackButton,
|
||||
IonButton,
|
||||
IonButtons,
|
||||
IonCol,
|
||||
IonContent,
|
||||
IonGrid,
|
||||
IonHeader,
|
||||
IonIcon,
|
||||
IonInput,
|
||||
IonItem,
|
||||
IonLabel,
|
||||
IonList,
|
||||
IonMenuButton,
|
||||
IonPage,
|
||||
IonPopover,
|
||||
IonRow,
|
||||
IonText,
|
||||
IonTitle,
|
||||
IonToolbar,
|
||||
useIonRouter,
|
||||
useIonViewDidEnter,
|
||||
} from '@ionic/react';
|
||||
import { star, starOutline, checkmarkDoneOutline } 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 { ImgPrinterSvg, ImgRiceSvg } from 'src/pages/tabs/carousell_me/settings/svgs';
|
||||
|
||||
interface AboutProps {}
|
||||
|
||||
const MyProfile: React.FC<AboutProps> = () => {
|
||||
const [showPopover, setShowPopover] = useState(false);
|
||||
const [popoverEvent, setPopoverEvent] = useState<MouseEvent>();
|
||||
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<HTMLDivElement>(null);
|
||||
const [swipeType, setSwipeType] = useState();
|
||||
const [deltaX, setDeltaX] = useState();
|
||||
const [velocityX, setVelocityX] = useState();
|
||||
const [swipeVerdict, setSwipeVerdict] = useState('helloworld');
|
||||
|
||||
const route = useIonRouter();
|
||||
const [username, setUsername] = useState('hello user');
|
||||
const [usernameError, setUsernameError] = useState(false);
|
||||
|
||||
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 () => {
|
||||
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 { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<IonPage id="my-profile-page">
|
||||
<IonHeader translucent={true}>
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonBackButton defaultHref="/tabs/schedule"></IonBackButton>
|
||||
</IonButtons>
|
||||
|
||||
<IonButtons slot="end">
|
||||
<IonButton>
|
||||
<IonIcon slot="icon-only" icon={checkmarkDoneOutline}></IonIcon>
|
||||
</IonButton>
|
||||
</IonButtons>
|
||||
|
||||
<IonTitle>{t('My Profile')}</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent fullscreen={true}>
|
||||
<IonHeader collapse="condense">
|
||||
<IonToolbar>
|
||||
<IonTitle size="large">{t('My Profile')}</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonGrid fixed>
|
||||
<IonList>
|
||||
<IonItem lines="none">
|
||||
<IonLabel style={{ fontWeight: 'bold' }}>Public Profile</IonLabel>
|
||||
</IonItem>
|
||||
<IonItem lines="none">
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
gap: '1rem',
|
||||
}}
|
||||
>
|
||||
<div>Username</div>
|
||||
<div>
|
||||
<IonInput placeholder="username"></IonInput>
|
||||
</div>
|
||||
</div>
|
||||
</IonItem>
|
||||
<IonItem lines="full">
|
||||
<IonLabel>Public Profile</IonLabel>
|
||||
</IonItem>
|
||||
</IonList>
|
||||
</IonGrid>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(MyProfile);
|
@@ -0,0 +1,2 @@
|
||||
#sample-blank-page {
|
||||
}
|
@@ -0,0 +1,159 @@
|
||||
import {
|
||||
IonBackButton,
|
||||
IonButton,
|
||||
IonButtons,
|
||||
IonContent,
|
||||
IonHeader,
|
||||
IonIcon,
|
||||
IonPage,
|
||||
IonPopover,
|
||||
IonTitle,
|
||||
IonToolbar,
|
||||
useIonRouter,
|
||||
useIonViewDidEnter,
|
||||
} from '@ionic/react';
|
||||
import { 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';
|
||||
|
||||
interface AboutProps {}
|
||||
|
||||
const MyProfile: React.FC<AboutProps> = () => {
|
||||
const [showPopover, setShowPopover] = useState(false);
|
||||
const [popoverEvent, setPopoverEvent] = useState<MouseEvent>();
|
||||
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<HTMLDivElement>(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 () => {
|
||||
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!")';
|
||||
|
||||
return (
|
||||
<IonPage id="sample-blank-page">
|
||||
<IonHeader translucent={true}>
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonBackButton defaultHref="/tabs/schedule"></IonBackButton>
|
||||
</IonButtons>
|
||||
<IonTitle>
|
||||
<h3 style={{ fontWeight: 'bold', fontSize: '0.9rem' }}>Offers Made</h3>
|
||||
</IonTitle>
|
||||
|
||||
<IonButtons slot="end">
|
||||
<IonButton onClick={() => {}}>
|
||||
{false ? (
|
||||
<IonIcon slot="icon-only" icon={star}></IonIcon>
|
||||
) : (
|
||||
<IonIcon slot="icon-only" icon={starOutline}></IonIcon>
|
||||
)}
|
||||
</IonButton>
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<IonContent>My Profile</IonContent>
|
||||
{/*
|
||||
<IonPopover isOpen={showPopover} event={popoverEvent} onDidDismiss={() => setShowPopover(false)}>
|
||||
<AboutPopover dismiss={() => setShowPopover(false)} />
|
||||
</IonPopover>
|
||||
*/}
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(MyProfile);
|
@@ -0,0 +1,2 @@
|
||||
#sample-blank-page {
|
||||
}
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 7.8 KiB |
207
03_source/mobile/src/pages/CarousellMe/ReviewsContent/index.tsx
Normal file
207
03_source/mobile/src/pages/CarousellMe/ReviewsContent/index.tsx
Normal file
@@ -0,0 +1,207 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import StarRatings from 'react-star-ratings';
|
||||
|
||||
import {
|
||||
IonHeader,
|
||||
IonTitle,
|
||||
IonToolbar,
|
||||
IonContent,
|
||||
IonPage,
|
||||
IonButtons,
|
||||
IonBadge,
|
||||
IonMenuButton,
|
||||
IonButton,
|
||||
IonIcon,
|
||||
IonDatetime,
|
||||
IonSelectOption,
|
||||
IonList,
|
||||
IonItem,
|
||||
IonLabel,
|
||||
IonSelect,
|
||||
IonPopover,
|
||||
IonText,
|
||||
IonCard,
|
||||
IonCardHeader,
|
||||
IonCardTitle,
|
||||
IonCardSubtitle,
|
||||
IonCardContent,
|
||||
IonTabs,
|
||||
IonTabBar,
|
||||
IonTabButton,
|
||||
IonSegment,
|
||||
IonSegmentButton,
|
||||
useIonRouter,
|
||||
IonAvatar,
|
||||
IonModal,
|
||||
} from '@ionic/react';
|
||||
import './style.scss';
|
||||
import {
|
||||
locationSharp,
|
||||
settingsOutline,
|
||||
qrCode,
|
||||
shareSocialOutline,
|
||||
ellipsisHorizontal,
|
||||
ellipsisVertical,
|
||||
logoFacebook,
|
||||
phonePortraitSharp,
|
||||
pricetagSharp,
|
||||
archiveSharp,
|
||||
calendar,
|
||||
personCircle,
|
||||
map,
|
||||
informationCircle,
|
||||
mailSharp,
|
||||
chevronDownSharp,
|
||||
chevronForwardSharp,
|
||||
} from 'ionicons/icons';
|
||||
// import AboutPopover from '../../components/AboutPopover';
|
||||
import { format, parseISO } from 'date-fns';
|
||||
|
||||
import './style.scss';
|
||||
import AmazingChatSvg from './AmazingChat.svg';
|
||||
|
||||
import lookForVisitorSvg from './look_for_visitor.svg';
|
||||
|
||||
type SessionListProps = {
|
||||
hide: boolean;
|
||||
};
|
||||
|
||||
const SampleReview: React.FC = () => {
|
||||
const modal = useRef<HTMLIonModalElement>(null);
|
||||
|
||||
function dismiss() {
|
||||
modal.current?.dismiss();
|
||||
}
|
||||
|
||||
let [openExplain, setOpenExplain] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<IonModal id="explain-review" ref={modal} isOpen={openExplain}>
|
||||
<div
|
||||
className="wrapper"
|
||||
style={{
|
||||
padding: '1rem',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
gap: '0.5rem',
|
||||
}}
|
||||
>
|
||||
<h1>Dialog header</h1>
|
||||
|
||||
<img src={AmazingChatSvg} width="33%" />
|
||||
|
||||
<div>
|
||||
<IonText style={{ fontSize: '1.2rem', fontWeight: 'bold' }}>Amazing chat</IonText>
|
||||
</div>
|
||||
<div style={{ fontSize: '0.9rem' }}>
|
||||
<p>You enjoyed chatting with this seller,</p>
|
||||
<p>it felt like you were talking to and old friend</p>
|
||||
</div>
|
||||
|
||||
<IonButton size="default" expand="block" onClick={dismiss}>
|
||||
Got it!
|
||||
</IonButton>
|
||||
</div>
|
||||
</IonModal>
|
||||
|
||||
<div id="sample-review">
|
||||
<div style={{ padding: '1rem', display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
gap: '1rem',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<IonAvatar style={{ height: '50px', width: '50px' }}>
|
||||
<img
|
||||
alt="Silhouette of a person's head"
|
||||
src="https://ionicframework.com/docs/img/demos/avatar.svg"
|
||||
/>
|
||||
</IonAvatar>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
|
||||
<IonText>Man1130</IonText>
|
||||
<IonText style={{ fontSize: '0.8rem' }} color="dark">
|
||||
<StarRatings
|
||||
rating={3}
|
||||
starRatedColor="green"
|
||||
changeRating={() => {}}
|
||||
numberOfStars={5}
|
||||
name="rating"
|
||||
starDimension="1rem"
|
||||
starSpacing="0px"
|
||||
//
|
||||
/>
|
||||
review from buyer 2 months ago
|
||||
</IonText>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ fontSize: '0.8rem' }}>
|
||||
<IonButton size="small" fill="outline" onClick={(e) => setOpenExplain(true)}>
|
||||
<IonIcon slot="start" icon={chevronForwardSharp}></IonIcon>
|
||||
Amazing
|
||||
</IonButton>
|
||||
<IonButton size="small" fill="outline">
|
||||
<IonIcon slot="start" icon={chevronForwardSharp}></IonIcon>
|
||||
Knows
|
||||
</IonButton>
|
||||
<IonButton size="small" fill="outline">
|
||||
<IonIcon slot="start" icon={chevronForwardSharp}></IonIcon>
|
||||
extra mile
|
||||
</IonButton>
|
||||
</div>
|
||||
|
||||
<IonText>除左幫我寫左個program之外仲幫左我好多 願意答問題同幫我解惑</IonText>
|
||||
|
||||
<div
|
||||
style={{
|
||||
padding: '0.5rem',
|
||||
borderRadius: '0.5rem',
|
||||
backgroundColor: 'rgb(240, 241, 241)',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-start',
|
||||
gap: '0.5rem',
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="https://upload.wikimedia.org/wikipedia/commons/6/6a/JavaScript-logo.png"
|
||||
style={{ width: '60px', height: '60px' }}
|
||||
/>
|
||||
<div style={{ fontSize: '0.8rem' }}>
|
||||
#html #css #開發 #指導 #代做 #電子平台 #programming #coding #javascript #python #react
|
||||
#debug HK$100
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const ReviewsContent: React.FC<SessionListProps> = ({ hide }) => {
|
||||
let route = useIonRouter();
|
||||
|
||||
if (hide) return <></>;
|
||||
return (
|
||||
<>
|
||||
<div id="review-list">
|
||||
<SampleReview />
|
||||
<SampleReview />
|
||||
<SampleReview />
|
||||
<SampleReview />
|
||||
<SampleReview />
|
||||
<SampleReview />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReviewsContent;
|
@@ -0,0 +1,26 @@
|
||||
ion-modal#explain-review {
|
||||
--width: 75vw;
|
||||
--min-width: 250px;
|
||||
--height: fit-content;
|
||||
--border-radius: 6px;
|
||||
--box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
ion-modal#explain-review h1 {
|
||||
margin: 20px 20px 10px 20px;
|
||||
}
|
||||
|
||||
ion-modal#explain-review ion-icon {
|
||||
margin-right: 6px;
|
||||
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
|
||||
padding: 4px 0;
|
||||
|
||||
color: #aaaaaa;
|
||||
}
|
||||
|
||||
ion-modal#explain-review .wrapper {
|
||||
margin-bottom: 10px;
|
||||
}
|
358
03_source/mobile/src/pages/CarousellMe/index.tsx
Normal file
358
03_source/mobile/src/pages/CarousellMe/index.tsx
Normal file
@@ -0,0 +1,358 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import StarRatings from 'react-star-ratings';
|
||||
import * as selectors from '../../data/selectors';
|
||||
|
||||
import {
|
||||
IonHeader,
|
||||
IonTitle,
|
||||
IonToolbar,
|
||||
IonContent,
|
||||
IonPage,
|
||||
IonButtons,
|
||||
IonBadge,
|
||||
IonMenuButton,
|
||||
IonButton,
|
||||
IonIcon,
|
||||
IonDatetime,
|
||||
IonSelectOption,
|
||||
IonList,
|
||||
IonItem,
|
||||
IonLabel,
|
||||
IonSelect,
|
||||
IonPopover,
|
||||
IonText,
|
||||
IonCard,
|
||||
IonCardHeader,
|
||||
IonCardTitle,
|
||||
IonCardSubtitle,
|
||||
IonCardContent,
|
||||
IonTabs,
|
||||
IonTabBar,
|
||||
IonTabButton,
|
||||
IonSegment,
|
||||
IonSegmentButton,
|
||||
useIonRouter,
|
||||
} from '@ionic/react';
|
||||
import './style.scss';
|
||||
import {
|
||||
locationSharp,
|
||||
settingsOutline,
|
||||
qrCode,
|
||||
shareSocialOutline,
|
||||
ellipsisHorizontal,
|
||||
ellipsisVertical,
|
||||
logoFacebook,
|
||||
phonePortraitSharp,
|
||||
pricetagSharp,
|
||||
archiveSharp,
|
||||
calendar,
|
||||
personCircle,
|
||||
map,
|
||||
informationCircle,
|
||||
mailSharp,
|
||||
chevronDownSharp,
|
||||
chevronForwardSharp,
|
||||
heartSharp,
|
||||
heartOutline,
|
||||
chatbubblesOutline,
|
||||
} from 'ionicons/icons';
|
||||
import AboutPopover from '../../components/AboutPopover';
|
||||
import { format, parseISO } from 'date-fns';
|
||||
|
||||
import lookForVisitorSvg from './look_for_visitor.svg';
|
||||
|
||||
import AboutContent from './AboutContent';
|
||||
import ReviewsContent from './ReviewsContent';
|
||||
import ListingsContent from './ListingsContent';
|
||||
import { triggerShare } from '../../util/triggerShare';
|
||||
import { connect } from '../../data/connect';
|
||||
// import { toProperCase } from 'src/util/toProperCase';
|
||||
|
||||
interface OwnProps {}
|
||||
|
||||
interface StateProps {
|
||||
events: Event[];
|
||||
}
|
||||
|
||||
interface DispatchProps {}
|
||||
|
||||
interface CarousellMeProps extends OwnProps, StateProps, DispatchProps {}
|
||||
|
||||
const CarousellMe: React.FC<CarousellMeProps> = () => {
|
||||
const [showPopover, setShowPopover] = useState(false);
|
||||
const [popoverEvent, setPopoverEvent] = useState<MouseEvent>();
|
||||
const [location, setLocation] = useState<'madison' | 'austin' | 'chicago' | 'seattle'>('madison');
|
||||
const [conferenceDate, setConferenceDate] = useState('2047-05-17T00:00:00-05:00');
|
||||
const { t } = useTranslation();
|
||||
|
||||
const route = useIonRouter();
|
||||
|
||||
const selectOptions = {
|
||||
header: 'Select a Location',
|
||||
};
|
||||
|
||||
const presentPopover = (e: React.MouseEvent) => {
|
||||
setPopoverEvent(e.nativeEvent);
|
||||
setShowPopover(true);
|
||||
};
|
||||
|
||||
function displayDate(date: string, dateFormat: string) {
|
||||
return format(parseISO(date), dateFormat);
|
||||
}
|
||||
|
||||
const changeRating = () => {
|
||||
console.log('change rating');
|
||||
};
|
||||
|
||||
// listing , reviews, about
|
||||
const [segment, setSegment] = useState<'listings' | 'reviews' | 'about'>('listings');
|
||||
|
||||
const handleSettingPageClick = () => {
|
||||
route.push('/tabs/carousell_me/settings');
|
||||
};
|
||||
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const handleShareLink = useCallback(() => {
|
||||
triggerShare(`https://louiscklaw.github.io`, 'louis portfolio')
|
||||
.then(() => {
|
||||
if (navigator.clipboard) setIsCopied(true);
|
||||
console.debug('share:copied');
|
||||
})
|
||||
.finally(() => {
|
||||
console.log('share:end');
|
||||
// setIsOpen(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<IonPage id="carousell-me-page" style={{}}>
|
||||
<IonContent style={{ paddingBottom: '5rem' }}>
|
||||
<IonHeader className="ion-no-border">
|
||||
<IonToolbar>
|
||||
<IonButtons slot="end">
|
||||
<IonButton onClick={presentPopover}>
|
||||
<IonIcon slot="icon-only" icon={heartOutline}></IonIcon>
|
||||
</IonButton>
|
||||
<IonButton onClick={presentPopover}>
|
||||
<IonIcon slot="icon-only" icon={chatbubblesOutline}></IonIcon>
|
||||
<IonBadge style={{ backgroundColor: 'white', color: 'black' }}>1</IonBadge>
|
||||
</IonButton>
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<div className="about-header">
|
||||
{/* Instead of loading an image each time the select changes, use opacity to transition them */}
|
||||
<div
|
||||
className="about-image madison"
|
||||
style={{
|
||||
//
|
||||
opacity: location === 'madison' ? '1' : undefined,
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '1.5rem',
|
||||
paddingLeft: '1rem',
|
||||
paddingRight: '1rem',
|
||||
}}
|
||||
>
|
||||
<div className="profile-test">
|
||||
<img
|
||||
src="https://avatars.githubusercontent.com/u/1376093"
|
||||
style={{
|
||||
height: '75px',
|
||||
width: '75px',
|
||||
borderRadius: '50px',
|
||||
border: '3px solid white',
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="setting-icons">
|
||||
<IonButton fill="clear" onClick={handleShareLink}>
|
||||
<IonIcon
|
||||
color="primary"
|
||||
slot="icon-only"
|
||||
ios={shareSocialOutline}
|
||||
md={shareSocialOutline}
|
||||
></IonIcon>
|
||||
</IonButton>
|
||||
|
||||
<IonButton fill="clear" routerLink="/tabs/carousell_me/qr_page">
|
||||
<IonIcon color="primary" slot="icon-only" ios={qrCode} md={qrCode}></IonIcon>
|
||||
</IonButton>
|
||||
|
||||
<IonButton
|
||||
fill="clear"
|
||||
routerLink="/tabs/carousell_me/settings"
|
||||
// onClick={handleSettingPageClick}
|
||||
>
|
||||
<IonIcon
|
||||
color="primary"
|
||||
slot="icon-only"
|
||||
ios={settingsOutline}
|
||||
md={settingsOutline}
|
||||
></IonIcon>
|
||||
</IonButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
|
||||
<div style={{ fontWeight: 'bold' }}>Louis Law</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '0.9rem',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '0.8rem',
|
||||
}}
|
||||
>
|
||||
<IonText color="medium">@louiscklaw</IonText>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'base-line',
|
||||
gap: '0.2rem',
|
||||
}}
|
||||
>
|
||||
<IonText color="medium">5.0</IonText>
|
||||
<StarRatings
|
||||
rating={3}
|
||||
starRatedColor="green"
|
||||
changeRating={changeRating}
|
||||
numberOfStars={5}
|
||||
name="rating"
|
||||
starDimension="1rem"
|
||||
starSpacing="0px"
|
||||
//
|
||||
/>
|
||||
<IonText color="medium">(12)</IonText>
|
||||
|
||||
<IonText color="medium">Joined 5y 6m</IonText>
|
||||
</div>
|
||||
|
||||
{/* location */}
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-start',
|
||||
gap: '1rem',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: '0.2rem',
|
||||
}}
|
||||
>
|
||||
<IonText color="medium">Verified</IonText>
|
||||
<IonIcon color="primary" ios={logoFacebook} md={logoFacebook}></IonIcon>
|
||||
<IonIcon
|
||||
color="primary"
|
||||
ios={phonePortraitSharp}
|
||||
md={phonePortraitSharp}
|
||||
></IonIcon>
|
||||
<IonIcon color="primary" ios={mailSharp} md={mailSharp}></IonIcon>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<IonText color="medium">
|
||||
<IonIcon ios={locationSharp} md={locationSharp}></IonIcon>
|
||||
Hong Kong
|
||||
</IonText>
|
||||
</div>
|
||||
</div>
|
||||
{/* location */}
|
||||
|
||||
{/* conbutton and caroubiz */}
|
||||
<div>
|
||||
<IonButton fill="outline" color="primary" shape="round" size="small">
|
||||
<IonIcon color="primary" ios={pricetagSharp} md={pricetagSharp}></IonIcon>
|
||||
409
|
||||
</IonButton>
|
||||
|
||||
<IonButton fill="outline" color="primary" shape="round" size="small">
|
||||
<IonIcon color="primary" ios={archiveSharp} md={archiveSharp}></IonIcon>
|
||||
caroubiz
|
||||
</IonButton>
|
||||
</div>
|
||||
{/* conbutton and caroubiz */}
|
||||
|
||||
{/* profile visitor */}
|
||||
<IonCard
|
||||
button
|
||||
onClick={(e) => route.push('/tabs/carousell_me/insights')}
|
||||
style={{ margin: '0rem' }}
|
||||
>
|
||||
<IonCardContent>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<img src={lookForVisitorSvg} height="20%" width="20%" />
|
||||
<div style={{ marginLeft: '1rem' }}>
|
||||
<IonText title="helloworld">
|
||||
<h3 style={{ color: 'black', fontWeight: 'bold' }}>
|
||||
No profile visitors today
|
||||
</h3>
|
||||
</IonText>
|
||||
<IonText>
|
||||
<h6 style={{ fontSize: '0.7rem' }}>List an item to get more visitors</h6>
|
||||
</IonText>
|
||||
</div>
|
||||
</div>
|
||||
</IonCardContent>
|
||||
</IonCard>
|
||||
{/* profile visitor */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* listing / reviews / about */}
|
||||
<IonSegment
|
||||
mode={'md'}
|
||||
value={segment}
|
||||
onIonChange={(e) => setSegment(e.detail.value as any)}
|
||||
>
|
||||
<IonSegmentButton value="listings">{t('Listings')}</IonSegmentButton>
|
||||
<IonSegmentButton value="reviews">{t('Reviwes')}</IonSegmentButton>
|
||||
<IonSegmentButton value="about">{t('About')}</IonSegmentButton>
|
||||
</IonSegment>
|
||||
{/* listing / reviews / about */}
|
||||
|
||||
<ListingsContent hide={segment != 'listings'} />
|
||||
<ReviewsContent hide={segment != 'reviews'} />
|
||||
<AboutContent hide={segment != 'about'} />
|
||||
</IonContent>
|
||||
|
||||
{/*
|
||||
<IonPopover isOpen={showPopover} event={popoverEvent} onDidDismiss={() => setShowPopover(false)}>
|
||||
<AboutPopover dismiss={() => setShowPopover(false)} />
|
||||
</IonPopover>
|
||||
*/}
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
// export default React.memo(CarousellMe);
|
||||
|
||||
export default connect<OwnProps, StateProps, DispatchProps>({
|
||||
mapStateToProps: (state) => ({
|
||||
events: selectors.getEvents(state),
|
||||
}),
|
||||
component: React.memo(CarousellMe),
|
||||
});
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.4 KiB |
118
03_source/mobile/src/pages/CarousellMe/style.scss
Normal file
118
03_source/mobile/src/pages/CarousellMe/style.scss
Normal file
@@ -0,0 +1,118 @@
|
||||
#carousell-me-page {
|
||||
.setting-icons {
|
||||
ion-button::part(native) {
|
||||
// https://ionicframework.com/docs/theming/css-shadow-parts
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
ion-content {
|
||||
--padding-bottom: 5rem;
|
||||
}
|
||||
|
||||
ion-toolbar {
|
||||
position: absolute;
|
||||
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
--background: transparent;
|
||||
--color: white;
|
||||
}
|
||||
|
||||
ion-toolbar ion-back-button,
|
||||
ion-toolbar ion-button,
|
||||
ion-toolbar ion-menu-button {
|
||||
--color: white;
|
||||
}
|
||||
|
||||
.about-header {
|
||||
position: relative;
|
||||
|
||||
width: 100%;
|
||||
height: 15%;
|
||||
}
|
||||
|
||||
.about-header .about-image {
|
||||
position: absolute;
|
||||
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
opacity: 0;
|
||||
|
||||
transition: opacity 500ms ease-in-out;
|
||||
}
|
||||
|
||||
.about-header .madison {
|
||||
background-image: url('https://cdn.dribbble.com/users/438226/screenshots/16695232/media/145ec731468579d04291d11c4c389c65.png');
|
||||
filter: brightness(75%);
|
||||
}
|
||||
|
||||
.about-info {
|
||||
position: absolute;
|
||||
margin-top: -10px;
|
||||
border-radius: 10px;
|
||||
background: var(--ion-background-color, #fff);
|
||||
}
|
||||
|
||||
.about-info h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.about-info ion-list {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.about-info p {
|
||||
line-height: 130%;
|
||||
|
||||
color: var(--ion-color-dark);
|
||||
}
|
||||
|
||||
.about-info ion-icon {
|
||||
margin-inline-end: 32px;
|
||||
}
|
||||
|
||||
/*
|
||||
* iOS Only
|
||||
*/
|
||||
|
||||
.ios .about-info {
|
||||
--ion-padding: 19px;
|
||||
}
|
||||
|
||||
.ios .about-info h3 {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.profile-test {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
|
||||
position: relative;
|
||||
height: 50px;
|
||||
|
||||
ion-icon {
|
||||
font-size: 1.5rem;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#date-input-popover {
|
||||
--offset-y: -var(--ion-safe-area-bottom);
|
||||
|
||||
--max-width: 90%;
|
||||
--width: 336px;
|
||||
}
|
@@ -19,6 +19,7 @@ import PATHS from '../PATHS';
|
||||
import Favourites from './Favourites';
|
||||
import Helloworld from './Helloworld';
|
||||
import TabAppRoute from '../TabAppRoute';
|
||||
import CarousellMe from './CarousellMe';
|
||||
|
||||
interface MainTabsProps {}
|
||||
|
||||
@@ -43,6 +44,16 @@ const MainTabs: React.FC<MainTabsProps> = () => {
|
||||
<Route path="/tabs/about" render={() => <About />} exact={true} />
|
||||
<Route path="/tabs/helloworld" render={() => <Helloworld />} exact={true} />
|
||||
|
||||
{/* */}
|
||||
<Route path={PATHS.CAROUSELL_ME} render={() => <CarousellMe />} exact={true} />
|
||||
{/*
|
||||
<Route path="/tabs/carousell_me/insights" component={Insights} exact={true} />
|
||||
<Route path="/tabs/carousell_me/OffersMade" component={OffersMade} exact={true} />
|
||||
<Route path="/tabs/carousell_me/settings" component={Settings} exact={true} />
|
||||
<Route path="/tabs/carousell_me/my_profile" component={MyProfile} exact={true} />
|
||||
<Route path="/tabs/carousell_me/qr_page" component={QRPage} exact={true} />
|
||||
*/}
|
||||
|
||||
{/* */}
|
||||
<TabAppRoute />
|
||||
</IonRouterOutlet>
|
||||
@@ -63,10 +74,14 @@ const MainTabs: React.FC<MainTabsProps> = () => {
|
||||
<IonIcon icon={people} />
|
||||
<IonLabel>Nearby</IonLabel>
|
||||
</IonTabButton>
|
||||
{/*
|
||||
<IonTabButton tab="orders" href={PATHS.ORDERS_LIST}>
|
||||
<IonIcon icon={location} />
|
||||
<IonLabel>Order</IonLabel>
|
||||
</IonTabButton>
|
||||
*/}
|
||||
<IonTabButton disabled></IonTabButton>
|
||||
|
||||
<IonTabButton tab="message" href={PATHS.MESSAGE_LIST}>
|
||||
<IonIcon icon={informationCircle} />
|
||||
<IonLabel>Message</IonLabel>
|
||||
|
5
03_source/mobile/src/util/toProperCase.tsx
Normal file
5
03_source/mobile/src/util/toProperCase.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
export const toProperCase = (txt: string) => {
|
||||
return txt.replace(/\w\S*/g, function (txt) {
|
||||
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
||||
});
|
||||
};
|
8
03_source/mobile/src/util/triggerShare.tsx
Normal file
8
03_source/mobile/src/util/triggerShare.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
export const triggerShare = (url: string, title: string) => {
|
||||
if (navigator.share) {
|
||||
return navigator?.share({ title, url });
|
||||
} else if (navigator.clipboard) {
|
||||
return navigator.clipboard.writeText(url);
|
||||
}
|
||||
return new Promise((resolve) => resolve(''));
|
||||
};
|
1
03_source/mobile/src/utils/helloworld.ts
Normal file
1
03_source/mobile/src/utils/helloworld.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const hello = 'world';
|
Reference in New Issue
Block a user