Compare commits

...

21 Commits

Author SHA1 Message Date
louiscklaw
e4b6c5e92d update profile example, 2025-06-06 14:11:26 +08:00
louiscklaw
03782cde24 update ordering app, 2025-06-06 14:07:30 +08:00
louiscklaw
7cc6d939f5 update ordering app, 2025-06-06 14:01:03 +08:00
louiscklaw
3bcb40c5ef update kanban, 2025-06-06 13:44:21 +08:00
louiscklaw
43a285dd2c update intsagram demo, 2025-06-06 13:29:35 +08:00
louiscklaw
09a8dc539e update floating action button, 2025-06-06 13:16:31 +08:00
louiscklaw
8f75226763 update floating tab bar, 2025-06-06 13:09:59 +08:00
louiscklaw
7cfbcc1573 update fastfood, 2025-06-06 12:55:43 +08:00
louiscklaw
5234ac06cd update, 2025-06-06 12:41:25 +08:00
louiscklaw
aec59fb328 update, 2025-06-06 12:03:26 +08:00
louiscklaw
7f9b4c2224 Merge branch 'develop/mobile/update-add-demo-pages/demo-capacitor-google-maps-tutorial/trunk' into develop/mobile/update-add-demo-pages/demo-color-tutorial/troubhleshoop 2025-06-06 11:55:04 +08:00
louiscklaw
197b006df3 update, 2025-06-06 11:52:20 +08:00
louiscklaw
3692b2204b update, 2025-06-06 11:51:58 +08:00
louiscklaw
3217a8d594 update, 2025-06-06 11:50:25 +08:00
louiscklaw
d453144500 update App.tsx, 2025-06-06 11:47:14 +08:00
louiscklaw
e8d12f34e8 update, 2025-06-06 11:45:58 +08:00
louiscklaw
56d43062c9 update, 2025-06-05 14:05:24 +08:00
louiscklaw
c2a02cff77 Merge branch 'develop/mobile/update-add-demo-pages/demo-accordion-tutorial/trunk' into develop/mobile/update-add-demo-pages/demo-banking-ui/trunk 2025-06-05 13:53:46 +08:00
louiscklaw
d3ef280b20 update, 2025-06-05 13:51:25 +08:00
louiscklaw
b2e9616178 update, 2025-06-05 13:15:03 +08:00
louiscklaw
d909805283 update, 2025-06-05 11:29:42 +08:00
620 changed files with 41450 additions and 522 deletions

View File

@@ -3,13 +3,5 @@
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"printWidth": 160,
"overrides": [
{
"files": "src/App.tsx",
"options": {
"printWidth": 240
}
}
]
}
"printWidth": 100
}

View File

@@ -12,7 +12,9 @@ dependencies {
implementation project(':capacitor-barcode-scanner')
implementation project(':capacitor-clipboard')
implementation project(':capacitor-geolocation')
implementation project(':capacitor-google-maps')
implementation project(':capacitor-preferences')
implementation project(':capacitor-share')
}

View File

@@ -11,5 +11,11 @@ project(':capacitor-clipboard').projectDir = new File('../node_modules/@capacito
include ':capacitor-geolocation'
project(':capacitor-geolocation').projectDir = new File('../node_modules/@capacitor/geolocation/android')
include ':capacitor-google-maps'
project(':capacitor-google-maps').projectDir = new File('../node_modules/@capacitor/google-maps/android')
include ':capacitor-preferences'
project(':capacitor-preferences').projectDir = new File('../node_modules/@capacitor/preferences/android')
include ':capacitor-share'
project(':capacitor-share').projectDir = new File('../node_modules/@capacitor/share/android')

View File

@@ -14,7 +14,9 @@ def capacitor_pods
pod 'CapacitorBarcodeScanner', :path => '../../node_modules/@capacitor/barcode-scanner'
pod 'CapacitorClipboard', :path => '../../node_modules/@capacitor/clipboard'
pod 'CapacitorGeolocation', :path => '../../node_modules/@capacitor/geolocation'
pod 'CapacitorGoogleMaps', :path => '../../node_modules/@capacitor/google-maps'
pod 'CapacitorPreferences', :path => '../../node_modules/@capacitor/preferences'
pod 'CapacitorShare', :path => '../../node_modules/@capacitor/share'
end
target 'App' do

View File

@@ -11,8 +11,10 @@
"@capacitor/clipboard": "^7.0.1",
"@capacitor/core": "^7.0.0",
"@capacitor/geolocation": "^7.1.2",
"@capacitor/google-maps": "^7.0.2",
"@capacitor/ios": "7.0.1",
"@capacitor/preferences": "^7.0.0",
"@capacitor/share": "^7.0.1",
"@hookform/resolvers": "^4.1.3",
"@ionic/react": "^8.5.0",
"@ionic/react-router": "^8.5.0",
@@ -27,6 +29,7 @@
"pigeon-maps": "^0.22.1",
"pullstate": "^1",
"react": "19.0.0",
"react-color": "^2.19.3",
"react-confetti": "^6.4.0",
"react-dom": "19.0.0",
"react-hook-form": "^7.55.0",
@@ -47,7 +50,7 @@
},
"scripts": {
"start": "npm run dev",
"dev": "vite --host 0.0.0.0 --cors",
"dev": "vite --force --host 0.0.0.0 --cors",
"ionic:serve": "vite",
"ionic:build": "tsc && vite build",
"build": "tsc && vite build",

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -0,0 +1 @@
<svg width="350" height="140" xmlns="http://www.w3.org/2000/svg" style="background:#f6f7f9"><g fill="none" fill-rule="evenodd"><path fill="#F04141" style="mix-blend-mode:multiply" d="M61.905-34.23l96.194 54.51-66.982 54.512L22 34.887z"/><circle fill="#10DC60" style="mix-blend-mode:multiply" cx="155.5" cy="135.5" r="57.5"/><path fill="#3880FF" style="mix-blend-mode:multiply" d="M208.538 9.513l84.417 15.392L223.93 93.93z"/><path fill="#FFCE00" style="mix-blend-mode:multiply" d="M268.625 106.557l46.332-26.75 46.332 26.75v53.5l-46.332 26.75-46.332-26.75z"/><circle fill="#7044FF" style="mix-blend-mode:multiply" cx="299.5" cy="9.5" r="38.5"/><rect fill="#11D3EA" style="mix-blend-mode:multiply" transform="rotate(-60 148.47 37.886)" x="143.372" y="-7.056" width="10.196" height="89.884" rx="5.098"/><path d="M-25.389 74.253l84.86 8.107c5.498.525 9.53 5.407 9.004 10.905a10 10 0 0 1-.057.477l-12.36 85.671a10.002 10.002 0 0 1-11.634 8.42l-86.351-15.226c-5.44-.959-9.07-6.145-8.112-11.584l13.851-78.551a10 10 0 0 1 10.799-8.219z" fill="#7044FF" style="mix-blend-mode:multiply"/><circle fill="#0CD1E8" style="mix-blend-mode:multiply" cx="273.5" cy="106.5" r="20.5"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -0,0 +1 @@
<svg width="350" height="140" xmlns="http://www.w3.org/2000/svg" style="background:#f6f7f9"><g fill="none" fill-rule="evenodd"><path fill="#F04141" style="mix-blend-mode:multiply" d="M61.905-34.23l96.194 54.51-66.982 54.512L22 34.887z"/><circle fill="#10DC60" style="mix-blend-mode:multiply" cx="155.5" cy="135.5" r="57.5"/><path fill="#3880FF" style="mix-blend-mode:multiply" d="M208.538 9.513l84.417 15.392L223.93 93.93z"/><path fill="#FFCE00" style="mix-blend-mode:multiply" d="M268.625 106.557l46.332-26.75 46.332 26.75v53.5l-46.332 26.75-46.332-26.75z"/><circle fill="#7044FF" style="mix-blend-mode:multiply" cx="299.5" cy="9.5" r="38.5"/><rect fill="#11D3EA" style="mix-blend-mode:multiply" transform="rotate(-60 148.47 37.886)" x="143.372" y="-7.056" width="10.196" height="89.884" rx="5.098"/><path d="M-25.389 74.253l84.86 8.107c5.498.525 9.53 5.407 9.004 10.905a10 10 0 0 1-.057.477l-12.36 85.671a10.002 10.002 0 0 1-11.634 8.42l-86.351-15.226c-5.44-.959-9.07-6.145-8.112-11.584l13.851-78.551a10 10 0 0 1 10.799-8.219z" fill="#7044FF" style="mix-blend-mode:multiply"/><circle fill="#0CD1E8" style="mix-blend-mode:multiply" cx="273.5" cy="106.5" r="20.5"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

@@ -0,0 +1 @@
<svg width="350" height="140" xmlns="http://www.w3.org/2000/svg" style="background:#f6f7f9"><g fill="none" fill-rule="evenodd"><path fill="#F04141" style="mix-blend-mode:multiply" d="M61.905-34.23l96.194 54.51-66.982 54.512L22 34.887z"/><circle fill="#10DC60" style="mix-blend-mode:multiply" cx="155.5" cy="135.5" r="57.5"/><path fill="#3880FF" style="mix-blend-mode:multiply" d="M208.538 9.513l84.417 15.392L223.93 93.93z"/><path fill="#FFCE00" style="mix-blend-mode:multiply" d="M268.625 106.557l46.332-26.75 46.332 26.75v53.5l-46.332 26.75-46.332-26.75z"/><circle fill="#7044FF" style="mix-blend-mode:multiply" cx="299.5" cy="9.5" r="38.5"/><rect fill="#11D3EA" style="mix-blend-mode:multiply" transform="rotate(-60 148.47 37.886)" x="143.372" y="-7.056" width="10.196" height="89.884" rx="5.098"/><path d="M-25.389 74.253l84.86 8.107c5.498.525 9.53 5.407 9.004 10.905a10 10 0 0 1-.057.477l-12.36 85.671a10.002 10.002 0 0 1-11.634 8.42l-86.351-15.226c-5.44-.959-9.07-6.145-8.112-11.584l13.851-78.551a10 10 0 0 1 10.799-8.219z" fill="#7044FF" style="mix-blend-mode:multiply"/><circle fill="#0CD1E8" style="mix-blend-mode:multiply" cx="273.5" cy="106.5" r="20.5"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

View File

@@ -65,7 +65,8 @@ import paths from './paths';
import PrivacyAgreement from './pages/PrivacyAgreement';
import AppRoute from './AppRoute';
//
import DemoReactShop from './pages/DemoReactShop';
// TODO: resume DemoReactShop
// import DemoReactShop from './pages/DemoReactShop';
import DemoWeatherApp from './pages/DemoWeatherApp';
import DemoClubHouse from './pages/DemoClubHouse';
import DemoScoreBoard from './pages/DemoScoreBoard';
@@ -78,6 +79,25 @@ import DemoDictionaryApp from './pages/DemoDictionaryApp';
// demo-recipe-app
import DemoRecipeApp from './pages/DemoRecipeApp';
// DemoSlidingProfile
import DemoSlidingProfile from './pages/DemoSlidingProfile';
// DemoQuizApp
import DemoQuizApp from './pages/DemoQuizApp';
import DemoBlogPostUi from './pages/DemoBlogPostUi';
import DemoReactTravelApp from './pages/DemoReactTravelApp';
import DemoPinterestFloatingTabBar from './pages/DemoPinterestFloatingTabBar';
import DemoRestaurantFinder from './pages/DemoRestaurantFinder';
import DemoReactOverlayHooks from './pages/DemoReactOverlayHooks';
import DemoReactSwitchTabs from './pages/DemoReactSwitchTabs';
import DemoReactPollApp from './pages/DemoReactPollApp';
import DemoReactWhatsAppClone from './pages/DemoReactWhatsAppClone';
import Demo2FaExample from './pages/Demo2FaExample';
import DemoAccordionTutorial from './pages/DemoAccordionTutorial';
import DemoBankingUi from './pages/DemoBankingUi';
import DemoCapacitorGoogleMapsTutorial from './pages/DemoCapacitorGoogleMapsTutorial';
import DemoColorTutorial from './pages/DemoColorTutorial';
setupIonicReact();
const App: React.FC = () => {
@@ -132,19 +152,100 @@ const IonicApp: React.FC<IonicAppProps> = ({
<AppRoute />
{/* */}
<Route path="/tabs" render={() => <MainTabs />} />
<Route path={paths.DEMO_REACT_SHOP} render={() => <DemoReactShop />} />
<Route path={paths.DEMO_WEATHER_APP} render={() => <DemoWeatherApp />} />
<Route path={paths.DEMO_CLUB_HOUSE} render={() => <DemoClubHouse />} />
<Route path={paths.DEMO_SCORE_BOARD} render={() => <DemoScoreBoard />} />
<Route path={paths.DEMO_QUOTE_APP} render={() => <DemoQuoteApp />} />
<Route path={paths.DEMO_QR_SCANNER} render={() => <DemoQrScanner />} />
<Route path={paths.DEMO_SHOP_APP_UI} render={() => <DemoShopAppUi />} />
<Route path={paths.DEMO_DICTIONARY_APP} render={() => <DemoDictionaryApp />} />
<Route path={paths.DEMO_RECIPE_APP} render={() => <DemoRecipeApp />} />
{/* */}
{/* */}
{/* */}
{/* */}
{/* */}
{/* */}
{/* */}
{/* */}
{/* */}
<Route path={paths.DEMO_ACCORDION_TUTORIAL} render={() => <DemoAccordionTutorial />} />
{/* */}
{/* */}
{/* */}
{/* */}
{/* */}
{/* */}
<Route path={paths.DEMO_BANKING_UI} render={() => <DemoBankingUi />} />
<Route
path={paths.DEMO_CAPACITOR_GOOGLE_MAPS_TUTORIAL}
render={() => <DemoCapacitorGoogleMapsTutorial />}
/>
<Route path={paths.DEMO_COLOR_TUTORIAL} render={() => <DemoColorTutorial />} />
{/*
<Route path={paths.DEMO_ECOMMERCE_EXAMPLE} render={() => <DemoEcommerceExample />} />
<Route path={paths.DEMO_FACEBOOK_CLONE} render={() => <DemoFacebookClone />} />
<Route path={paths.DEMO_FAST_FOOD_APP} render={() => <DemoFastFoodApp />} />
<Route path={paths.DEMO_FLOATING_TABS} render={() => <DemoFloatingTabs />} />
<Route path={paths.DEMO_INSTAGRAM_CLONE} render={() => <DemoInstagramClone />} />
<Route path={paths.DEMO_KANBAN_BOARD} render={() => <DemoKanbanBoard />} />
<Route path={paths.DEMO_ORDERING_APP} render={() => <DemoOrderingApp />} />
<Route path={paths.DEMO_PROFILE_EXAMPLE} render={() => <DemoProfileExample />} />
<Route path={paths.DEMO_PULLSTATE_TUTORIAL} render={() => <DemoPullstateTutorial />} />
<Route path={paths.DEMO_REACT_ADD_TO_CART} render={() => <DemoReactAddToCart />} />
<Route path={paths.DEMO_REACT_CALCULATOR} render={() => <DemoReactCalculator />} />
<Route path={paths.DEMO_REACT_DRAWING_CANVAS} render={() => <DemoReactDrawingCanvas />} />
<Route path={paths.DEMO_REACT_HOOK_FORM_EXAMPLE} render={() => <DemoReactHookFormExample />} />
<Route path={paths.DEMO_REACT_ITEM_LIST} render={() => <DemoReactItemList />} />
<Route path={paths.DEMO_REACT_LIFECYCLES} render={() => <DemoReactLifecycles />} />
<Route path={paths.DEMO_REACT_LOGIN} render={() => <DemoReactLogin />} />
<Route path={paths.DEMO_REACT_MARVEL_APP} render={() => <DemoReactMarvelApp />} />
<Route path={paths.DEMO_REACT_MOVIE_APP_WITH_ALGOLIA} render(() => <DemoReactMovieAppWithAlgolia />} />
<Route path={paths.DEMO_REACT_NOTES} render={() => <DemoReactNotes />} />
<Route path={paths.DEMO_REACT_ONBOARDING_UI} render={() => <DemoReactOnboardingUI />} />
<Route path={paths.DEMO_REACT_PROFILE_DASHBOARD_UI} render(() => <DemoReactProfileDashboardUI />} />
<Route path={paths.DEMO_REACT_QR_CODE} render={() => <DemoReactQRCode />} />
<Route path={paths.DEMO_REACT_QUOTES} render(() => <DemoReactQuotes />} />
<Route path={paths.DEMO_REACT_SHOP_UI} render(() => <DemoReactShopUI />} />
<Route path={paths.DEMO_REACT_TABS_MENUS_CUSTOM} render(() => <DemoReactTabsMenusCustom />} />
<Route path={paths.DEMO_REACT_THEME_SWITCHER} render(() => <DemoReactThemeSwitcher />} />
<Route path={paths.DEMO_REACT_WHATSAPP_CLONE} render(() => <DemoReactWhatsAppClone />} />
<Route path={paths.DEMO_SKELETON_TEXT} render(() => <DemoSkeletonText />} />
<Route path={paths.DEMO_STICKY_BOTTOM_SHEET_EXAMPLE} render(() => <DemoStickyBottomSheetExample />} />
<Route path={paths.DEMO_STORAGE_EXAMPLE} render(() => <DemoStorageExample />} />
<Route path={paths.DEMO_SWIPERJS_TUTORIAL} render(() => <DemoSwiperjsTutorial />} />
<Route path={paths.DEMO_WEATHER_APP_UI} render(() => <DemoWeatherAppUI />} />
*/}
<Route path={paths.DEMO_2FA_EXAMPLE} render={() => <Demo2FaExample />} />
{/* have problemx` */}
{/* <Route path={paths.DEMO_REACT_WHATSAPP_CLONE} render={() => <DemoReactWhatsAppClone />} /> */}
<Route path={paths.DEMO_REACT_POLL_APP} render={() => <DemoReactPollApp />} />
<Route path={paths.DEMO_BLOG_POST_UI} render={() => <DemoBlogPostUi />} />
<Route path={paths.DEMO_CLUB_HOUSE} render={() => <DemoClubHouse />} />
<Route path={paths.DEMO_DICTIONARY_APP} render={() => <DemoDictionaryApp />} />
<Route
path={paths.DEMO_PINTEREST_FLOATING_TAB_BAR}
render={() => <DemoPinterestFloatingTabBar />}
/>
<Route path={paths.DEMO_QR_SCANNER} render={() => <DemoQrScanner />} />
<Route path={paths.DEMO_QUIZ_APP} render={() => <DemoQuizApp />} />
<Route path={paths.DEMO_QUOTE_APP} render={() => <DemoQuoteApp />} />
<Route path={paths.DEMO_REACT_OVERLAY_HOOKS} render={() => <DemoReactOverlayHooks />} />
<Route path={paths.DEMO_REACT_POLL_APP} render={() => <DemoReactPollApp />} />
{/* TODO: resume DemoReactShop */}
{/* <Route path={paths.DEMO_REACT_SHOP} render={() => <DemoReactShop />} /> */}
<Route path={paths.DEMO_REACT_SWITCH_TABS} render={() => <DemoReactSwitchTabs />} />
<Route path={paths.DEMO_REACT_TRAVEL_APP} render={() => <DemoReactTravelApp />} />
<Route path={paths.DEMO_RECIPE_APP} render={() => <DemoRecipeApp />} />
<Route path={paths.DEMO_RESTAURANT_FINDER} render={() => <DemoRestaurantFinder />} />
<Route path={paths.DEMO_SCORE_BOARD} render={() => <DemoScoreBoard />} />
<Route path={paths.DEMO_SHOP_APP_UI} render={() => <DemoShopAppUi />} />
<Route path={paths.DEMO_SLIDING_PROFILE} render={() => <DemoSlidingProfile />} />
<Route path="/account" component={Account} />
<Route path="/login" component={Login} />
<Route path="/mylogin" component={MyLogin} />
@@ -153,7 +254,6 @@ const IonicApp: React.FC<IonicAppProps> = ({
<Route path="/support" component={Support} />
<Route path="/tutorial" component={Tutorial} />
{/* */}
<Route
path="/logout"
render={() => {

View File

@@ -0,0 +1,96 @@
import {
IonButton,
IonButtons,
IonCol,
IonContent,
IonHeader,
IonIcon,
IonPage,
IonRow,
IonTitle,
IonToolbar,
useIonRouter,
} from '@ionic/react';
import { Geolocation } from '@capacitor/geolocation';
import { useEffect, useState } from 'react';
import { SkeletonDashboard } from '../components/SkeletonDashboard';
import { chevronBackOutline, refreshOutline } from 'ionicons/icons';
import { CurrentWeather } from '../components/CurrentWeather';
function Tab1() {
const router = useIonRouter();
const [currentWeather, setCurrentWeather] = useState(false);
useEffect(() => {
getCurrentPosition();
}, []);
const getCurrentPosition = async () => {
setCurrentWeather(false);
const coordinates = await Geolocation.getCurrentPosition();
getAddress(coordinates.coords);
};
const getAddress = async (coords) => {
const query = `${coords.latitude},${coords.longitude}`;
const response = await fetch(
`https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}`
);
const data = await response.json();
console.log(data);
setCurrentWeather(data);
};
// const router = useIonRouter();
function handleBackClick() {
router.goBack();
}
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>My Weather</IonTitle>
<IonButtons slot="end">
<IonButton onClick={() => getCurrentPosition()}>
<IonIcon icon={refreshOutline} color="primary" />
</IonButton>
</IonButtons>
<IonButtons slot="start">
<IonButton onClick={() => handleBackClick()}>
<IonIcon icon={chevronBackOutline} color="primary" />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Dashboard</IonTitle>
</IonToolbar>
</IonHeader>
<IonRow className="ion-margin-start ion-margin-end ion-justify-content-center ion-text-center">
<IonCol size="12">
<h4>Here's your location based weather</h4>
</IonCol>
</IonRow>
<div style={{ marginTop: '-1.5rem' }}>
{currentWeather ? (
<CurrentWeather currentWeather={currentWeather} />
) : (
<SkeletonDashboard />
)}
</div>
</IonContent>
</IonPage>
);
}
export default Tab1;

View File

@@ -0,0 +1,81 @@
import {
IonButton,
IonCol,
IonContent,
IonHeader,
IonPage,
IonRow,
IonSearchbar,
IonTitle,
IonToolbar,
} from '@ionic/react';
import { useState } from 'react';
import { CurrentWeather } from '../components/CurrentWeather';
function Tab2() {
const [search, setSearch] = useState('');
const [currentWeather, setCurrentWeather] = useState(false);
const performSearch = async () => {
getAddress(search);
};
const getAddress = async (city) => {
const response = await fetch(
`https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no`
);
const data = await response.json();
if (data && data.current && data.location) {
setCurrentWeather(data);
}
};
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Search</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Search</IonTitle>
</IonToolbar>
</IonHeader>
<IonRow className="ion-justify-content-center ion-margin-top ion-align-items-center">
<IonCol size="7">
<IonSearchbar
placeholder="Try 'London'"
animated
value={search}
onIonChange={(e) => setSearch(e.target.value)}
/>
</IonCol>
<IonCol size="5">
<IonButton
expand="block"
className="ion-margin-start ion-margin-end"
onClick={performSearch}
>
Search
</IonButton>
</IonCol>
</IonRow>
<div style={{ marginTop: '-0.8rem' }}>
{currentWeather ? (
<CurrentWeather currentWeather={currentWeather} />
) : (
<h3 className="ion-text-center">Your search result will appear here</h3>
)}
</div>
</IonContent>
</IonPage>
);
}
export default Tab2;

View File

@@ -0,0 +1,62 @@
import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react';
import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons';
import { useEffect, useState } from 'react';
export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => {
const [property, setProperty] = useState(false);
const properties = {
wind: {
isIcon: false,
icon: '/assets/WeatherDemo/wind.png',
alt: 'wind',
label: 'Wind',
value: `${currentWeather.current.wind_mph}mph`,
},
feelsLike: {
isIcon: true,
icon: thermometerOutline,
alt: 'feels like',
label: 'Feels like',
value: `${currentWeather.current.feelslike_c}°C`,
},
indexUV: {
isIcon: true,
icon: sunnyOutline,
alt: 'index uv',
label: 'Index UV',
value: currentWeather.current.uv,
},
pressure: {
isIcon: true,
icon: pulseOutline,
alt: 'pressure',
label: 'Pressure',
value: `${currentWeather.current.pressure_mb} mbar`,
},
};
useEffect(() => {
setProperty(properties[type]);
}, [type]);
return (
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
{!property.isIcon && (
<img alt={property.alt} src={property.icon} height="32" width="32" />
)}
{property.isIcon && (
<IonIcon icon={property.icon} color="medium" style={{ fontSize: '2rem' }} />
)}
</IonCol>
<IonCol size="9">
<IonCardSubtitle>{property.label}</IonCardSubtitle>
<IonNote>{property.value}</IonNote>
</IonCol>
</IonRow>
</IonCol>
);
};

View File

@@ -0,0 +1,48 @@
import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react';
import { WeatherProperty } from './WeatherProperty';
export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => (
<IonGrid>
<IonCard>
<IonCardContent className="ion-text-center">
<IonText color="primary">
<h1>
{currentWeather.location.region},{' '}
<span style={{ color: 'gray' }}>{currentWeather.location.country}</span>
</h1>
</IonText>
<div className="ion-margin-top">
<img
alt="condition"
src={currentWeather.current.condition.icon.replace('//', 'https://')}
/>
<IonText color="dark">
<h1 style={{ fontWeight: 'bold' }}>{currentWeather.current.condition.text}</h1>
</IonText>
<IonText color="medium">
<p>{new Date(currentWeather.location.localtime).toDateString()}</p>
</IonText>
</div>
<IonCardTitle style={{ fontSize: '3rem' }} className="ion-margin-top">
{currentWeather.current.temp_c}&#8451;
</IonCardTitle>
<IonGrid className="ion-margin-top">
<IonRow>
<WeatherProperty type="wind" currentWeather={currentWeather} />
<WeatherProperty type="feelsLike" currentWeather={currentWeather} />
</IonRow>
<IonRow className="ion-margin-top">
<WeatherProperty type="indexUV" currentWeather={currentWeather} />
<WeatherProperty type="pressure" currentWeather={currentWeather} />
</IonRow>
</IonGrid>
</IonCardContent>
</IonCard>
</IonGrid>
);

View File

@@ -0,0 +1,117 @@
import {
IonCard,
IonCardContent,
IonCardSubtitle,
IonCardTitle,
IonCol,
IonGrid,
IonIcon,
IonNote,
IonRow,
IonSkeletonText,
IonText,
IonThumbnail,
} from '@ionic/react';
import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons';
export const SkeletonDashboard = () => (
<IonGrid>
<IonCard>
<IonCardContent className="ion-text-center">
<IonText color="primary">
<h1>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</h1>
</IonText>
<div className="ion-margin-top">
<IonThumbnail>
<IonSkeletonText animated style={{ width: '2rem', height: '2rem' }} />
</IonThumbnail>
<IonText color="dark">
<h1 style={{ fontWeight: 'bold' }}>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</h1>
</IonText>
<IonText color="medium">
<p>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</p>
</IonText>
</div>
<IonCardTitle style={{ fontSize: '3rem' }} className="ion-margin-top">
<IonSkeletonText animated style={{ height: '3rem', width: '30%', textAlign: 'center' }} />
</IonCardTitle>
<IonGrid className="ion-margin-top">
<IonRow>
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
<img alt="wind" src="/assets/WeatherDemo/wind.png" height="32" width="32" />
</IonCol>
<IonCol size="9">
<IonCardSubtitle>Wind</IonCardSubtitle>
<IonNote>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</IonNote>
</IonCol>
</IonRow>
</IonCol>
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
<IonIcon icon={thermometerOutline} color="medium" style={{ fontSize: '2rem' }} />
</IonCol>
<IonCol size="9">
<IonCardSubtitle>Feels like</IonCardSubtitle>
<IonNote>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</IonNote>
</IonCol>
</IonRow>
</IonCol>
</IonRow>
<IonRow className="ion-margin-top">
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
<IonIcon icon={sunnyOutline} color="medium" style={{ fontSize: '2rem' }} />
</IonCol>
<IonCol size="9">
<IonCardSubtitle>Index UV</IonCardSubtitle>
<IonNote>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</IonNote>
</IonCol>
</IonRow>
</IonCol>
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
<IonIcon icon={pulseOutline} color="medium" style={{ fontSize: '2rem' }} />
</IonCol>
<IonCol size="9">
<IonCardSubtitle>Pressure</IonCardSubtitle>
<IonNote>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</IonNote>
</IonCol>
</IonRow>
</IonCol>
</IonRow>
</IonGrid>
</IonCardContent>
</IonCard>
</IonGrid>
);

View File

@@ -0,0 +1,40 @@
import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
import { cloudOutline, searchOutline } from 'ionicons/icons';
import { Route, Redirect } from 'react-router';
import Tab1 from './AppPages/Tab1';
import Tab2 from './AppPages/Tab2';
import './style.scss';
function Demo2FaExample() {
return (
<IonTabs>
<IonRouterOutlet>
<Route exact path="/demo-weather-app/tab1">
<Tab1 />
</Route>
<Route exact path="/demo-weather-app/tab2">
<Tab2 />
</Route>
<Redirect exact path="/demo-weather-app" to="/demo-weather-app/tab1" />
</IonRouterOutlet>
{/* */}
<IonTabBar slot="bottom">
<IonTabButton tab="tab1" href="/demo-weather-app/tab1">
<IonIcon icon={cloudOutline} />
<IonLabel>Dashboard</IonLabel>
</IonTabButton>
<IonTabButton tab="tab2" href="/demo-weather-app/tab2">
<IonIcon icon={searchOutline} />
<IonLabel>Search</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
);
}
export default Demo2FaExample;

View File

@@ -0,0 +1,103 @@
#about-page {
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: 30%;
}
.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('/assets/WeatherDemo/img/about/madison.jpg');
}
.about-header .austin {
background-image: url('/assets/WeatherDemo/img/about/austin.jpg');
}
.about-header .chicago {
background-image: url('/assets/WeatherDemo/img/about/chicago.jpg');
}
.about-header .seattle {
background-image: url('/assets/WeatherDemo/img/about/seattle.jpg');
}
.about-info {
position: relative;
margin-top: -10px;
border-radius: 10px;
background: var(--ion-background-color, #fff);
z-index: 2; // display rounded border above header image
}
.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;
}
}
#date-input-popover {
--offset-y: -var(--ion-safe-area-bottom);
--max-width: 90%;
--width: 336px;
}

View File

@@ -0,0 +1,96 @@
import {
IonButton,
IonButtons,
IonCol,
IonContent,
IonHeader,
IonIcon,
IonPage,
IonRow,
IonTitle,
IonToolbar,
useIonRouter,
} from '@ionic/react';
import { Geolocation } from '@capacitor/geolocation';
import { useEffect, useState } from 'react';
import { SkeletonDashboard } from '../components/SkeletonDashboard';
import { chevronBackOutline, refreshOutline } from 'ionicons/icons';
import { CurrentWeather } from '../components/CurrentWeather';
function Tab1() {
const router = useIonRouter();
const [currentWeather, setCurrentWeather] = useState(false);
useEffect(() => {
getCurrentPosition();
}, []);
const getCurrentPosition = async () => {
setCurrentWeather(false);
const coordinates = await Geolocation.getCurrentPosition();
getAddress(coordinates.coords);
};
const getAddress = async (coords) => {
const query = `${coords.latitude},${coords.longitude}`;
const response = await fetch(
`https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}`
);
const data = await response.json();
console.log(data);
setCurrentWeather(data);
};
// const router = useIonRouter();
function handleBackClick() {
router.goBack();
}
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>My Weather</IonTitle>
<IonButtons slot="end">
<IonButton onClick={() => getCurrentPosition()}>
<IonIcon icon={refreshOutline} color="primary" />
</IonButton>
</IonButtons>
<IonButtons slot="start">
<IonButton onClick={() => handleBackClick()}>
<IonIcon icon={chevronBackOutline} color="primary" />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Dashboard</IonTitle>
</IonToolbar>
</IonHeader>
<IonRow className="ion-margin-start ion-margin-end ion-justify-content-center ion-text-center">
<IonCol size="12">
<h4>Here's your location based weather</h4>
</IonCol>
</IonRow>
<div style={{ marginTop: '-1.5rem' }}>
{currentWeather ? (
<CurrentWeather currentWeather={currentWeather} />
) : (
<SkeletonDashboard />
)}
</div>
</IonContent>
</IonPage>
);
}
export default Tab1;

View File

@@ -0,0 +1,81 @@
import {
IonButton,
IonCol,
IonContent,
IonHeader,
IonPage,
IonRow,
IonSearchbar,
IonTitle,
IonToolbar,
} from '@ionic/react';
import { useState } from 'react';
import { CurrentWeather } from '../components/CurrentWeather';
function Tab2() {
const [search, setSearch] = useState('');
const [currentWeather, setCurrentWeather] = useState(false);
const performSearch = async () => {
getAddress(search);
};
const getAddress = async (city) => {
const response = await fetch(
`https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no`
);
const data = await response.json();
if (data && data.current && data.location) {
setCurrentWeather(data);
}
};
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Search</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Search</IonTitle>
</IonToolbar>
</IonHeader>
<IonRow className="ion-justify-content-center ion-margin-top ion-align-items-center">
<IonCol size="7">
<IonSearchbar
placeholder="Try 'London'"
animated
value={search}
onIonChange={(e) => setSearch(e.target.value)}
/>
</IonCol>
<IonCol size="5">
<IonButton
expand="block"
className="ion-margin-start ion-margin-end"
onClick={performSearch}
>
Search
</IonButton>
</IonCol>
</IonRow>
<div style={{ marginTop: '-0.8rem' }}>
{currentWeather ? (
<CurrentWeather currentWeather={currentWeather} />
) : (
<h3 className="ion-text-center">Your search result will appear here</h3>
)}
</div>
</IonContent>
</IonPage>
);
}
export default Tab2;

View File

@@ -0,0 +1,29 @@
import { IonAccordion, IonAccordionGroup, IonIcon, IonItem, IonLabel, IonList } from '@ionic/react';
import { topics } from '../data';
export const Accordion = () => {
return (
<IonAccordionGroup>
{topics.map((topic, index) => {
return (
<IonAccordion key={`accordion_${index}`} value={topic.header.toLowerCase()}>
<IonItem slot="header">
<IonIcon icon={topic.icon} color={topic.color} />
<IonLabel className="ion-padding-start">{topic.header}</IonLabel>
</IonItem>
<IonList slot="content">
{topic.options.map((option, index2) => {
return (
<IonItem key={`option_${index}_${index2}`} routerLink={`/topics/${option.label}`}>
<IonLabel>{option.label}</IonLabel>
</IonItem>
);
})}
</IonList>
</IonAccordion>
);
})}
</IonAccordionGroup>
);
};

View File

@@ -0,0 +1,30 @@
import { IonAccordion, IonAccordionGroup, IonIcon, IonItem, IonLabel, IonList } from '@ionic/react';
import { topics } from '../data';
import React from 'react';
export const Accordion: React.FC = () => {
return (
<IonAccordionGroup>
{topics.map((topic: any, index: number) => {
return (
<IonAccordion key={`accordion_${index}`} value={topic.header.toLowerCase()}>
<IonItem slot="header">
<IonIcon icon={topic.icon} color={topic.color} />
<IonLabel className="ion-padding-start">{topic.header}</IonLabel>
</IonItem>
<IonList slot="content">
{topic.options.map((option: any, index2: number) => {
return (
<IonItem key={`option_${index}_${index2}`} routerLink={`/topics/${option.label}`}>
<IonLabel>{option.label}</IonLabel>
</IonItem>
);
})}
</IonList>
</IonAccordion>
);
})}
</IonAccordionGroup>
);
};

View File

@@ -0,0 +1,62 @@
import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react';
import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons';
import { useEffect, useState } from 'react';
export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => {
const [property, setProperty] = useState(false);
const properties = {
wind: {
isIcon: false,
icon: '/assets/WeatherDemo/wind.png',
alt: 'wind',
label: 'Wind',
value: `${currentWeather.current.wind_mph}mph`,
},
feelsLike: {
isIcon: true,
icon: thermometerOutline,
alt: 'feels like',
label: 'Feels like',
value: `${currentWeather.current.feelslike_c}°C`,
},
indexUV: {
isIcon: true,
icon: sunnyOutline,
alt: 'index uv',
label: 'Index UV',
value: currentWeather.current.uv,
},
pressure: {
isIcon: true,
icon: pulseOutline,
alt: 'pressure',
label: 'Pressure',
value: `${currentWeather.current.pressure_mb} mbar`,
},
};
useEffect(() => {
setProperty(properties[type]);
}, [type]);
return (
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
{!property.isIcon && (
<img alt={property.alt} src={property.icon} height="32" width="32" />
)}
{property.isIcon && (
<IonIcon icon={property.icon} color="medium" style={{ fontSize: '2rem' }} />
)}
</IonCol>
<IonCol size="9">
<IonCardSubtitle>{property.label}</IonCardSubtitle>
<IonNote>{property.value}</IonNote>
</IonCol>
</IonRow>
</IonCol>
);
};

View File

@@ -0,0 +1,48 @@
import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react';
import { WeatherProperty } from './WeatherProperty';
export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => (
<IonGrid>
<IonCard>
<IonCardContent className="ion-text-center">
<IonText color="primary">
<h1>
{currentWeather.location.region},{' '}
<span style={{ color: 'gray' }}>{currentWeather.location.country}</span>
</h1>
</IonText>
<div className="ion-margin-top">
<img
alt="condition"
src={currentWeather.current.condition.icon.replace('//', 'https://')}
/>
<IonText color="dark">
<h1 style={{ fontWeight: 'bold' }}>{currentWeather.current.condition.text}</h1>
</IonText>
<IonText color="medium">
<p>{new Date(currentWeather.location.localtime).toDateString()}</p>
</IonText>
</div>
<IonCardTitle style={{ fontSize: '3rem' }} className="ion-margin-top">
{currentWeather.current.temp_c}&#8451;
</IonCardTitle>
<IonGrid className="ion-margin-top">
<IonRow>
<WeatherProperty type="wind" currentWeather={currentWeather} />
<WeatherProperty type="feelsLike" currentWeather={currentWeather} />
</IonRow>
<IonRow className="ion-margin-top">
<WeatherProperty type="indexUV" currentWeather={currentWeather} />
<WeatherProperty type="pressure" currentWeather={currentWeather} />
</IonRow>
</IonGrid>
</IonCardContent>
</IonCard>
</IonGrid>
);

View File

@@ -0,0 +1,117 @@
import {
IonCard,
IonCardContent,
IonCardSubtitle,
IonCardTitle,
IonCol,
IonGrid,
IonIcon,
IonNote,
IonRow,
IonSkeletonText,
IonText,
IonThumbnail,
} from '@ionic/react';
import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons';
export const SkeletonDashboard = () => (
<IonGrid>
<IonCard>
<IonCardContent className="ion-text-center">
<IonText color="primary">
<h1>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</h1>
</IonText>
<div className="ion-margin-top">
<IonThumbnail>
<IonSkeletonText animated style={{ width: '2rem', height: '2rem' }} />
</IonThumbnail>
<IonText color="dark">
<h1 style={{ fontWeight: 'bold' }}>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</h1>
</IonText>
<IonText color="medium">
<p>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</p>
</IonText>
</div>
<IonCardTitle style={{ fontSize: '3rem' }} className="ion-margin-top">
<IonSkeletonText animated style={{ height: '3rem', width: '30%', textAlign: 'center' }} />
</IonCardTitle>
<IonGrid className="ion-margin-top">
<IonRow>
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
<img alt="wind" src="/assets/WeatherDemo/wind.png" height="32" width="32" />
</IonCol>
<IonCol size="9">
<IonCardSubtitle>Wind</IonCardSubtitle>
<IonNote>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</IonNote>
</IonCol>
</IonRow>
</IonCol>
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
<IonIcon icon={thermometerOutline} color="medium" style={{ fontSize: '2rem' }} />
</IonCol>
<IonCol size="9">
<IonCardSubtitle>Feels like</IonCardSubtitle>
<IonNote>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</IonNote>
</IonCol>
</IonRow>
</IonCol>
</IonRow>
<IonRow className="ion-margin-top">
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
<IonIcon icon={sunnyOutline} color="medium" style={{ fontSize: '2rem' }} />
</IonCol>
<IonCol size="9">
<IonCardSubtitle>Index UV</IonCardSubtitle>
<IonNote>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</IonNote>
</IonCol>
</IonRow>
</IonCol>
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
<IonIcon icon={pulseOutline} color="medium" style={{ fontSize: '2rem' }} />
</IonCol>
<IonCol size="9">
<IonCardSubtitle>Pressure</IonCardSubtitle>
<IonNote>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</IonNote>
</IonCol>
</IonRow>
</IonCol>
</IonRow>
</IonGrid>
</IonCardContent>
</IonCard>
</IonGrid>
);

View File

@@ -0,0 +1,120 @@
import { bicycleOutline, fastFoodOutline, filmOutline, gameControllerOutline, libraryOutline } from 'ionicons/icons';
export const topics = [
{
header: 'Attractions',
color: 'primary',
icon: filmOutline,
options: [
{
label: 'Cinema',
},
{
label: 'Bowling Alley',
},
{
label: 'Crazy Golf',
},
{
label: 'Theme Park',
},
],
},
{
header: 'Dining',
color: 'success',
icon: fastFoodOutline,
options: [
{
label: 'Breakfast & Brunch',
},
{
label: 'New American',
},
{
label: 'Sushi Bars',
},
{
label: 'Filipino Food',
},
{
label: 'Asian Fusion',
},
{
label: 'Ramen Houses',
},
{
label: 'Dinner Venues',
},
],
},
{
header: 'Gaming',
color: 'warning',
icon: gameControllerOutline,
options: [
{
label: 'Xbox',
},
{
label: 'Playstation',
},
{
label: 'Nintendo Switch',
},
{
label: 'PC',
},
{
label: 'Mobile',
},
{
label: 'Dreamcast',
},
],
},
{
header: 'Exercise',
color: 'secondary',
icon: bicycleOutline,
options: [
{
label: 'Yoga',
},
{
label: 'Pilates',
},
{
label: 'Weight Training',
},
{
label: 'Cardio',
},
{
label: 'Zumba',
},
],
},
{
header: 'Education',
color: 'danger',
icon: libraryOutline,
options: [
{
label: 'School',
},
{
label: 'High School',
},
{
label: 'University Bachelors',
},
{
label: 'University Masters',
},
{
label: 'University pHD',
},
],
},
];

View File

@@ -0,0 +1,126 @@
import {
bicycleOutline,
fastFoodOutline,
filmOutline,
gameControllerOutline,
libraryOutline,
} from 'ionicons/icons';
export const topics: any = [
{
header: 'Attractions',
color: 'primary',
icon: filmOutline,
options: [
{
label: 'Cinema',
},
{
label: 'Bowling Alley',
},
{
label: 'Crazy Golf',
},
{
label: 'Theme Park',
},
],
},
{
header: 'Dining',
color: 'success',
icon: fastFoodOutline,
options: [
{
label: 'Breakfast & Brunch',
},
{
label: 'New American',
},
{
label: 'Sushi Bars',
},
{
label: 'Filipino Food',
},
{
label: 'Asian Fusion',
},
{
label: 'Ramen Houses',
},
{
label: 'Dinner Venues',
},
],
},
{
header: 'Gaming',
color: 'warning',
icon: gameControllerOutline,
options: [
{
label: 'Xbox',
},
{
label: 'Playstation',
},
{
label: 'Nintendo Switch',
},
{
label: 'PC',
},
{
label: 'Mobile',
},
{
label: 'Dreamcast',
},
],
},
{
header: 'Exercise',
color: 'secondary',
icon: bicycleOutline,
options: [
{
label: 'Yoga',
},
{
label: 'Pilates',
},
{
label: 'Weight Training',
},
{
label: 'Cardio',
},
{
label: 'Zumba',
},
],
},
{
header: 'Education',
color: 'danger',
icon: libraryOutline,
options: [
{
label: 'School',
},
{
label: 'High School',
},
{
label: 'University Bachelors',
},
{
label: 'University Masters',
},
{
label: 'University pHD',
},
],
},
];

View File

@@ -0,0 +1,56 @@
import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
import { cloudOutline, searchOutline } from 'ionicons/icons';
import { Route, Redirect } from 'react-router';
// import Tab1 from './AppPages/Tab1';
// import Tab2 from './AppPages/Tab2';
import Topic from './pages/Topic';
import Home from './pages/Home';
import './style.scss';
function DemoAccordionTutorial() {
return (
<IonTabs className="demo-accordion-tutorial">
<IonRouterOutlet>
{/*
<Route exact path="/demo-accordion-tutorial/tab1">
<Tab1 />
</Route>
<Route exact path="/demo-accordion-tutorial/tab2">
<Tab2 />
</Route>
*/}
<Route exact path="/demo-accordion-tutorial/home">
<Home />
</Route>
<Route exact path="/demo-accordion-tutorial/topics/:topic">
<Topic />
</Route>
<Redirect exact path="/demo-accordion-tutorial" to="/demo-accordion-tutorial/home" />
{/* <Redirect exact path="/demo-accordion-tutorial" to="/demo-accordion-tutorial/tab1" /> */}
</IonRouterOutlet>
{/*
<IonTabBar slot="bottom">
<IonTabButton tab="tab1" href="/demo-accordion-tutorial/tab1">
<IonIcon icon={cloudOutline} />
<IonLabel>Dashboard</IonLabel>
</IonTabButton>
<IonTabButton tab="tab2" href="/demo-accordion-tutorial/tab2">
<IonIcon icon={searchOutline} />
<IonLabel>Search</IonLabel>
</IonTabButton>
</IonTabBar>
*/}
</IonTabs>
);
}
export default DemoAccordionTutorial;

View File

@@ -0,0 +1,97 @@
import {
IonAccordion,
IonAccordionGroup,
IonButton,
IonButtons,
IonContent,
IonHeader,
IonIcon,
IonItem,
IonLabel,
IonList,
IonPage,
IonTitle,
IonToolbar,
useIonRouter,
} from '@ionic/react';
import { Accordion } from '../components/Accordion';
import { chevronBackOutline } from 'ionicons/icons';
const Home = () => {
const router = useIonRouter();
function handleBackClick() {
router.goBack();
}
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Accordion</IonTitle>
<IonButtons slot="start">
<IonButton onClick={() => handleBackClick()}>
<IonIcon icon={chevronBackOutline} color="primary" />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Accordion</IonTitle>
<IonButtons slot="start">
<IonButton onClick={() => handleBackClick()}>
<IonIcon icon={chevronBackOutline} color="primary" />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<Accordion />
{/* <IonAccordionGroup>
<IonAccordion>
<IonItem slot="header">
<IonLabel>Languages</IonLabel>
</IonItem>
<IonList slot="content">
<IonItem>
<IonLabel>English</IonLabel>
</IonItem>
<IonItem>
<IonLabel>Spanish</IonLabel>
</IonItem>
<IonItem>
<IonLabel>Italian</IonLabel>
</IonItem>
</IonList>
</IonAccordion>
<IonAccordion>
<IonItem slot="header">
<IonLabel>Languages 2</IonLabel>
</IonItem>
<IonList slot="content">
<IonItem>
<IonLabel>English</IonLabel>
</IonItem>
<IonItem>
<IonLabel>Spanish</IonLabel>
</IonItem>
<IonItem>
<IonLabel>Italian</IonLabel>
</IonItem>
</IonList>
</IonAccordion>
</IonAccordionGroup> */}
</IonContent>
</IonPage>
);
};
export default Home;

View File

@@ -0,0 +1,97 @@
import {
IonAccordion,
IonAccordionGroup,
IonButton,
IonButtons,
IonContent,
IonHeader,
IonIcon,
IonItem,
IonLabel,
IonList,
IonPage,
IonTitle,
IonToolbar,
useIonRouter,
} from '@ionic/react';
import { Accordion } from '../components/Accordion';
import { chevronBackOutline } from 'ionicons/icons';
const Home: React.FC = () => {
const router = useIonRouter();
function handleBackClick() {
router.goBack();
}
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Accordion</IonTitle>
<IonButtons slot="start">
<IonButton onClick={() => handleBackClick()}>
<IonIcon icon={chevronBackOutline} color="primary" />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Accordion</IonTitle>
<IonButtons slot="start">
<IonButton onClick={() => handleBackClick()}>
<IonIcon icon={chevronBackOutline} color="primary" />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<Accordion />
{/* <IonAccordionGroup>
<IonAccordion>
<IonItem slot="header">
<IonLabel>Languages</IonLabel>
</IonItem>
<IonList slot="content">
<IonItem>
<IonLabel>English</IonLabel>
</IonItem>
<IonItem>
<IonLabel>Spanish</IonLabel>
</IonItem>
<IonItem>
<IonLabel>Italian</IonLabel>
</IonItem>
</IonList>
</IonAccordion>
<IonAccordion>
<IonItem slot="header">
<IonLabel>Languages 2</IonLabel>
</IonItem>
<IonList slot="content">
<IonItem>
<IonLabel>English</IonLabel>
</IonItem>
<IonItem>
<IonLabel>Spanish</IonLabel>
</IonItem>
<IonItem>
<IonLabel>Italian</IonLabel>
</IonItem>
</IonList>
</IonAccordion>
</IonAccordionGroup> */}
</IonContent>
</IonPage>
);
};
export default Home;

View File

@@ -0,0 +1,39 @@
import { IonBackButton, IonButtons, IonCol, IonContent, IonGrid, IonHeader, IonLabel, IonPage, IonRow, IonTitle, IonToolbar } from '@ionic/react';
import { useParams } from 'react-router';
const Topic = () => {
const { topic } = useParams();
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonBackButton text="Topics" />
</IonButtons>
<IonTitle>{ topic }</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">{ topic }</IonTitle>
</IonToolbar>
</IonHeader>
<IonGrid>
<IonRow>
<IonCol size="12">
<IonLabel>This is the page for the topic: { topic }.</IonLabel>
</IonCol>
</IonRow>
</IonGrid>
</IonContent>
</IonPage>
);
};
export default Topic;

View File

@@ -0,0 +1,49 @@
import {
IonBackButton,
IonButtons,
IonCol,
IonContent,
IonGrid,
IonHeader,
IonLabel,
IonPage,
IonRow,
IonTitle,
IonToolbar,
} from '@ionic/react';
import { useParams } from 'react-router';
const Topic = () => {
const { topic } = useParams<{ topic: string }>();
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonBackButton text="Topics" />
</IonButtons>
<IonTitle>{topic}</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">{topic}</IonTitle>
</IonToolbar>
</IonHeader>
<IonGrid>
<IonRow>
<IonCol size="12">
<IonLabel>This is the page for the topic: {topic}.</IonLabel>
</IonCol>
</IonRow>
</IonGrid>
</IonContent>
</IonPage>
);
};
export default Topic;

View File

@@ -0,0 +1,237 @@
.demo-accordion-tutorial {
/* Ionic Variables and Theming. For more info, please see:
http://ionicframework.com/docs/theming/ */
/** Ionic CSS Variables **/
* {
/** primary **/
--ion-color-primary: #3880ff;
--ion-color-primary-rgb: 56, 128, 255;
--ion-color-primary-contrast: #ffffff;
--ion-color-primary-contrast-rgb: 255, 255, 255;
--ion-color-primary-shade: #3171e0;
--ion-color-primary-tint: #4c8dff;
/** secondary **/
--ion-color-secondary: #3dc2ff;
--ion-color-secondary-rgb: 61, 194, 255;
--ion-color-secondary-contrast: #ffffff;
--ion-color-secondary-contrast-rgb: 255, 255, 255;
--ion-color-secondary-shade: #36abe0;
--ion-color-secondary-tint: #50c8ff;
/** tertiary **/
--ion-color-tertiary: #5260ff;
--ion-color-tertiary-rgb: 82, 96, 255;
--ion-color-tertiary-contrast: #ffffff;
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
--ion-color-tertiary-shade: #4854e0;
--ion-color-tertiary-tint: #6370ff;
/** success **/
--ion-color-success: #2dd36f;
--ion-color-success-rgb: 45, 211, 111;
--ion-color-success-contrast: #ffffff;
--ion-color-success-contrast-rgb: 255, 255, 255;
--ion-color-success-shade: #28ba62;
--ion-color-success-tint: #42d77d;
/** warning **/
--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;
/** danger **/
--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;
/** dark **/
--ion-color-dark: #222428;
--ion-color-dark-rgb: 34, 36, 40;
--ion-color-dark-contrast: #ffffff;
--ion-color-dark-contrast-rgb: 255, 255, 255;
--ion-color-dark-shade: #1e2023;
--ion-color-dark-tint: #383a3e;
/** medium **/
--ion-color-medium: #92949c;
--ion-color-medium-rgb: 146, 148, 156;
--ion-color-medium-contrast: #ffffff;
--ion-color-medium-contrast-rgb: 255, 255, 255;
--ion-color-medium-shade: #808289;
--ion-color-medium-tint: #9d9fa6;
/** light **/
--ion-color-light: #f4f5f8;
--ion-color-light-rgb: 244, 245, 248;
--ion-color-light-contrast: #000000;
--ion-color-light-contrast-rgb: 0, 0, 0;
--ion-color-light-shade: #d7d8da;
--ion-color-light-tint: #f5f6f9;
}
@media (prefers-color-scheme: dark) {
/*
* Dark Colors
* -------------------------------------------
*/
body {
--ion-color-primary: #428cff;
--ion-color-primary-rgb: 66, 140, 255;
--ion-color-primary-contrast: #ffffff;
--ion-color-primary-contrast-rgb: 255, 255, 255;
--ion-color-primary-shade: #3a7be0;
--ion-color-primary-tint: #5598ff;
--ion-color-secondary: #50c8ff;
--ion-color-secondary-rgb: 80, 200, 255;
--ion-color-secondary-contrast: #ffffff;
--ion-color-secondary-contrast-rgb: 255, 255, 255;
--ion-color-secondary-shade: #46b0e0;
--ion-color-secondary-tint: #62ceff;
--ion-color-tertiary: #6a64ff;
--ion-color-tertiary-rgb: 106, 100, 255;
--ion-color-tertiary-contrast: #ffffff;
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
--ion-color-tertiary-shade: #5d58e0;
--ion-color-tertiary-tint: #7974ff;
--ion-color-success: #2fdf75;
--ion-color-success-rgb: 47, 223, 117;
--ion-color-success-contrast: #000000;
--ion-color-success-contrast-rgb: 0, 0, 0;
--ion-color-success-shade: #29c467;
--ion-color-success-tint: #44e283;
--ion-color-warning: #ffd534;
--ion-color-warning-rgb: 255, 213, 52;
--ion-color-warning-contrast: #000000;
--ion-color-warning-contrast-rgb: 0, 0, 0;
--ion-color-warning-shade: #e0bb2e;
--ion-color-warning-tint: #ffd948;
--ion-color-danger: #ff4961;
--ion-color-danger-rgb: 255, 73, 97;
--ion-color-danger-contrast: #ffffff;
--ion-color-danger-contrast-rgb: 255, 255, 255;
--ion-color-danger-shade: #e04055;
--ion-color-danger-tint: #ff5b71;
--ion-color-dark: #f4f5f8;
--ion-color-dark-rgb: 244, 245, 248;
--ion-color-dark-contrast: #000000;
--ion-color-dark-contrast-rgb: 0, 0, 0;
--ion-color-dark-shade: #d7d8da;
--ion-color-dark-tint: #f5f6f9;
--ion-color-medium: #989aa2;
--ion-color-medium-rgb: 152, 154, 162;
--ion-color-medium-contrast: #000000;
--ion-color-medium-contrast-rgb: 0, 0, 0;
--ion-color-medium-shade: #86888f;
--ion-color-medium-tint: #a2a4ab;
--ion-color-light: #222428;
--ion-color-light-rgb: 34, 36, 40;
--ion-color-light-contrast: #ffffff;
--ion-color-light-contrast-rgb: 255, 255, 255;
--ion-color-light-shade: #1e2023;
--ion-color-light-tint: #383a3e;
}
/*
* iOS Dark Theme
* -------------------------------------------
*/
.ios body {
--ion-background-color: #000000;
--ion-background-color-rgb: 0, 0, 0;
--ion-text-color: #ffffff;
--ion-text-color-rgb: 255, 255, 255;
--ion-color-step-50: #0d0d0d;
--ion-color-step-100: #1a1a1a;
--ion-color-step-150: #262626;
--ion-color-step-200: #333333;
--ion-color-step-250: #404040;
--ion-color-step-300: #4d4d4d;
--ion-color-step-350: #595959;
--ion-color-step-400: #666666;
--ion-color-step-450: #737373;
--ion-color-step-500: #808080;
--ion-color-step-550: #8c8c8c;
--ion-color-step-600: #999999;
--ion-color-step-650: #a6a6a6;
--ion-color-step-700: #b3b3b3;
--ion-color-step-750: #bfbfbf;
--ion-color-step-800: #cccccc;
--ion-color-step-850: #d9d9d9;
--ion-color-step-900: #e6e6e6;
--ion-color-step-950: #f2f2f2;
--ion-item-background: #000000;
--ion-card-background: #1c1c1d;
}
.ios ion-modal {
--ion-background-color: var(--ion-color-step-100);
--ion-toolbar-background: var(--ion-color-step-150);
--ion-toolbar-border-color: var(--ion-color-step-250);
}
/*
* Material Design Dark Theme
* -------------------------------------------
*/
.md body {
--ion-background-color: #121212;
--ion-background-color-rgb: 18, 18, 18;
--ion-text-color: #ffffff;
--ion-text-color-rgb: 255, 255, 255;
--ion-border-color: #222222;
--ion-color-step-50: #1e1e1e;
--ion-color-step-100: #2a2a2a;
--ion-color-step-150: #363636;
--ion-color-step-200: #414141;
--ion-color-step-250: #4d4d4d;
--ion-color-step-300: #595959;
--ion-color-step-350: #656565;
--ion-color-step-400: #717171;
--ion-color-step-450: #7d7d7d;
--ion-color-step-500: #898989;
--ion-color-step-550: #949494;
--ion-color-step-600: #a0a0a0;
--ion-color-step-650: #acacac;
--ion-color-step-700: #b8b8b8;
--ion-color-step-750: #c4c4c4;
--ion-color-step-800: #d0d0d0;
--ion-color-step-850: #dbdbdb;
--ion-color-step-900: #e7e7e7;
--ion-color-step-950: #f3f3f3;
--ion-item-background: #1e1e1e;
--ion-toolbar-background: #1f1f1f;
--ion-tab-bar-background: #1f1f1f;
--ion-card-background: #1e1e1e;
}
}
}

View File

@@ -0,0 +1,96 @@
import {
IonButton,
IonButtons,
IonCol,
IonContent,
IonHeader,
IonIcon,
IonPage,
IonRow,
IonTitle,
IonToolbar,
useIonRouter,
} from '@ionic/react';
import { Geolocation } from '@capacitor/geolocation';
import { useEffect, useState } from 'react';
import { SkeletonDashboard } from '../components/SkeletonDashboard';
import { chevronBackOutline, refreshOutline } from 'ionicons/icons';
import { CurrentWeather } from '../components/CurrentWeather';
function Tab1() {
const router = useIonRouter();
const [currentWeather, setCurrentWeather] = useState(false);
useEffect(() => {
getCurrentPosition();
}, []);
const getCurrentPosition = async () => {
setCurrentWeather(false);
const coordinates = await Geolocation.getCurrentPosition();
getAddress(coordinates.coords);
};
const getAddress = async (coords) => {
const query = `${coords.latitude},${coords.longitude}`;
const response = await fetch(
`https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}`
);
const data = await response.json();
console.log(data);
setCurrentWeather(data);
};
// const router = useIonRouter();
function handleBackClick() {
router.goBack();
}
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>My Weather</IonTitle>
<IonButtons slot="end">
<IonButton onClick={() => getCurrentPosition()}>
<IonIcon icon={refreshOutline} color="primary" />
</IonButton>
</IonButtons>
<IonButtons slot="start">
<IonButton onClick={() => handleBackClick()}>
<IonIcon icon={chevronBackOutline} color="primary" />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Dashboard</IonTitle>
</IonToolbar>
</IonHeader>
<IonRow className="ion-margin-start ion-margin-end ion-justify-content-center ion-text-center">
<IonCol size="12">
<h4>Here's your location based weather</h4>
</IonCol>
</IonRow>
<div style={{ marginTop: '-1.5rem' }}>
{currentWeather ? (
<CurrentWeather currentWeather={currentWeather} />
) : (
<SkeletonDashboard />
)}
</div>
</IonContent>
</IonPage>
);
}
export default Tab1;

View File

@@ -0,0 +1,81 @@
import {
IonButton,
IonCol,
IonContent,
IonHeader,
IonPage,
IonRow,
IonSearchbar,
IonTitle,
IonToolbar,
} from '@ionic/react';
import { useState } from 'react';
import { CurrentWeather } from '../components/CurrentWeather';
function Tab2() {
const [search, setSearch] = useState('');
const [currentWeather, setCurrentWeather] = useState(false);
const performSearch = async () => {
getAddress(search);
};
const getAddress = async (city) => {
const response = await fetch(
`https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no`
);
const data = await response.json();
if (data && data.current && data.location) {
setCurrentWeather(data);
}
};
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Search</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Search</IonTitle>
</IonToolbar>
</IonHeader>
<IonRow className="ion-justify-content-center ion-margin-top ion-align-items-center">
<IonCol size="7">
<IonSearchbar
placeholder="Try 'London'"
animated
value={search}
onIonChange={(e) => setSearch(e.target.value)}
/>
</IonCol>
<IonCol size="5">
<IonButton
expand="block"
className="ion-margin-start ion-margin-end"
onClick={performSearch}
>
Search
</IonButton>
</IonCol>
</IonRow>
<div style={{ marginTop: '-0.8rem' }}>
{currentWeather ? (
<CurrentWeather currentWeather={currentWeather} />
) : (
<h3 className="ion-text-center">Your search result will appear here</h3>
)}
</div>
</IonContent>
</IonPage>
);
}
export default Tab2;

View File

@@ -0,0 +1,75 @@
import { IonButton, IonCardSubtitle, IonCol, IonIcon, IonList, IonRow } from '@ionic/react';
import DebitCard from './DebitCard';
import styles from './CardSlide.module.css';
import TransactionItem from './TransactionItem';
import { addOutline, arrowRedoOutline } from 'ionicons/icons';
import { formatBalance } from '../data/Utils';
const CardSlide = (props) => {
const { index, card, profile } = props;
return (
<>
<IonRow className="ion-text-center">
<IonCol size="12">
<IonCardSubtitle color="medium">balance</IonCardSubtitle>
<IonCardSubtitle id={`slide_${index}_balance`} className={` ${styles.balance} animate__animated`}>
<span className={styles.poundSign}>£</span>
&nbsp;{formatBalance(card.balance)}
<IonButton
className={styles.addButton}
size="small"
style={{ '--background': card.color, '--background-focused': card.color, '--background-hover': card.color, '--background-activated': card.color }}
routerLink={`/add-transaction/${card.id}`}
>
<IonIcon icon={addOutline} />
</IonButton>
</IonCardSubtitle>
</IonCol>
</IonRow>
<IonRow id={`card_${index}_container`} className="animate__animated ion-text-center ion-justify-content-center">
<IonCol size="12">
<DebitCard key={index} {...card} profile={profile} />
</IonCol>
</IonRow>
<IonRow className={styles.heading}>
<IonCol size="12">
<h6>Transactions</h6>
</IonCol>
</IonRow>
{card.transactions.length > 0 && (
<IonRow id={`slide_${index}_transactions`} className="animate__animated">
<IonCol size="12">
<IonList className={styles.transactionList}>
{card.transactions.length > 0 &&
card.transactions
.slice(0)
.reverse()
.map((transaction, index) => <TransactionItem key={`card_transaction_${index}`} {...transaction} color={card.color} />)}
</IonList>
</IonCol>
</IonRow>
)}
{card.transactions.length === 0 && (
<IonRow id={`slide_${index}_transactions`} className="animate__animated">
<IonCol size="12">
<h5>No transactions found</h5>
<IonButton
style={{ '--background': card.color, '--background-focused': card.color, '--background-hover': card.color, '--background-activated': card.color }}
routerLink={`/add-transaction/${card.id}`}
>
<IonIcon icon={arrowRedoOutline} />
&nbsp;Transfer funds
</IonButton>
</IonCol>
</IonRow>
)}
</>
);
};
export default CardSlide;

View File

@@ -0,0 +1,51 @@
.customSlide {
display: flex;
flex-direction: column;
}
.transactionList {
overflow: scroll;
width: 100vw;
}
.balance {
font-weight: 300;
font-size: 1.5rem;
color: black;
display: flex;
flex-direction: row;
justify-content: center;
align-content: center;
align-items: center;
}
.poundSign {
font-weight: 800;
font-size: 1.2rem;
}
.heading h6 {
padding: 0;
margin: 0;
text-align: left !important;
float: left !important;
text-align: left !important;
color: rgb(124, 124, 124);
font-weight: 400;
}
.heading {
width: 83%;
padding: 0;
margin: 0;
margin-top: 0.75rem;
}
.addButton {
--border-radius: 500px !important;
width: fit-content !important;
margin-top: 0.45rem;
margin-left: 1rem;
opacity: 0.6;
}

View File

@@ -0,0 +1,62 @@
import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react';
import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons';
import { useEffect, useState } from 'react';
export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => {
const [property, setProperty] = useState(false);
const properties = {
wind: {
isIcon: false,
icon: '/assets/WeatherDemo/wind.png',
alt: 'wind',
label: 'Wind',
value: `${currentWeather.current.wind_mph}mph`,
},
feelsLike: {
isIcon: true,
icon: thermometerOutline,
alt: 'feels like',
label: 'Feels like',
value: `${currentWeather.current.feelslike_c}°C`,
},
indexUV: {
isIcon: true,
icon: sunnyOutline,
alt: 'index uv',
label: 'Index UV',
value: currentWeather.current.uv,
},
pressure: {
isIcon: true,
icon: pulseOutline,
alt: 'pressure',
label: 'Pressure',
value: `${currentWeather.current.pressure_mb} mbar`,
},
};
useEffect(() => {
setProperty(properties[type]);
}, [type]);
return (
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
{!property.isIcon && (
<img alt={property.alt} src={property.icon} height="32" width="32" />
)}
{property.isIcon && (
<IonIcon icon={property.icon} color="medium" style={{ fontSize: '2rem' }} />
)}
</IonCol>
<IonCol size="9">
<IonCardSubtitle>{property.label}</IonCardSubtitle>
<IonNote>{property.value}</IonNote>
</IonCol>
</IonRow>
</IonCol>
);
};

View File

@@ -0,0 +1,48 @@
import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react';
import { WeatherProperty } from './WeatherProperty';
export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => (
<IonGrid>
<IonCard>
<IonCardContent className="ion-text-center">
<IonText color="primary">
<h1>
{currentWeather.location.region},{' '}
<span style={{ color: 'gray' }}>{currentWeather.location.country}</span>
</h1>
</IonText>
<div className="ion-margin-top">
<img
alt="condition"
src={currentWeather.current.condition.icon.replace('//', 'https://')}
/>
<IonText color="dark">
<h1 style={{ fontWeight: 'bold' }}>{currentWeather.current.condition.text}</h1>
</IonText>
<IonText color="medium">
<p>{new Date(currentWeather.location.localtime).toDateString()}</p>
</IonText>
</div>
<IonCardTitle style={{ fontSize: '3rem' }} className="ion-margin-top">
{currentWeather.current.temp_c}&#8451;
</IonCardTitle>
<IonGrid className="ion-margin-top">
<IonRow>
<WeatherProperty type="wind" currentWeather={currentWeather} />
<WeatherProperty type="feelsLike" currentWeather={currentWeather} />
</IonRow>
<IonRow className="ion-margin-top">
<WeatherProperty type="indexUV" currentWeather={currentWeather} />
<WeatherProperty type="pressure" currentWeather={currentWeather} />
</IonRow>
</IonGrid>
</IonCardContent>
</IonCard>
</IonGrid>
);

View File

@@ -0,0 +1,48 @@
import { useEffect, useState } from 'react';
import styles from './DebitCard.module.css';
const DebitCard = (props) => {
const { type, number, profile, expiry, secret, color } = props;
const [lastFourCardNumbers, setLastFourCardNumbers] = useState('****');
const cardClass = `card_${color}`;
const cardTypeLogo = type === 'visa' ? '/visa.png' : '/mastercard.png';
useEffect(() => {
var lastFourNumbers = number ? number.substr(number.length - 4) : '1234';
setLastFourCardNumbers(lastFourNumbers);
}, [number]);
return (
<div className={styles.card}>
<div className={`${styles.card__front} ${styles.card__part} ${styles[cardClass]}`}>
<img className={`${styles.card__front_chip} ${styles.card__square}`} src="/chip.png" alt="1" />
<img className={`${styles.card__front_square} ${styles.card__square}`} src="/ionicwhite.png" alt="1" />
<img className={`${styles.card__front_logo} ${styles.card__logo}`} src={cardTypeLogo} alt="2" />
<p className={styles.card_number}>**** **** **** {lastFourCardNumbers}</p>
<div className={styles.card__space_75}>
<span className={styles.card__label}>Card holder</span>
<p className={styles.card__info}>{`${profile.firstname} ${profile.surname}`}</p>
</div>
<div className={styles.card__space_25}>
<span className={styles.card__label}>Expires</span>
<p className={styles.card__info}>{expiry}</p>
</div>
</div>
<div className={`${styles.card__back} ${styles.card__part} ${styles[cardClass]}`}>
<div className={styles.card__black_line}></div>
<div className={styles.card__back_content}>
<div className={styles.card__secret}>
<p className={styles.card__secret__last}>{secret}</p>
</div>
<img className={`${styles.card__back_square} ${styles.card__square}`} src="/ionicwhite.png" alt="3" />
<img className={`${styles.card__back_logo} ${styles.card__logo}`} src={cardTypeLogo} alt="5" />
</div>
</div>
</div>
);
};
export default DebitCard;

View File

@@ -0,0 +1,188 @@
@import url('https://fonts.googleapis.com/css?family=Space+Mono:400,400i,700,700i');
.card {
box-sizing: border-box;
font-family: 'Space Mono', monospace;
margin: 0 auto;
}
.title {
margin-bottom: 30px;
color: #162969;
}
.card {
width: 320px;
height: 190px;
-webkit-perspective: 600px;
-moz-perspective: 600px;
perspective: 600px;
}
.card__part {
box-shadow: 1px 1px #aaa3a3;
top: 0;
position: absolute;
z-index: 1000;
left: 0;
display: inline-block;
width: 320px;
height: 190px;
background-repeat: no-repeat;
background-position: center;
background-size: cover;
border-radius: 8px;
-webkit-transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
-moz-transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
-ms-transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
-o-transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
-webkit-transform-style: preserve-3d;
-moz-transform-style: preserve-3d;
-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
}
.card_orange {
background: linear-gradient(to right bottom, #fd696b, #fa616e, #f65871, #c74261, #d62158);
}
.card_blue {
background: linear-gradient(to right bottom, #699dfd, #61b5fa, #58aff6, #4b86c9, #2151d6);
}
.card_black {
background: linear-gradient(to right bottom, #292929, #363636, #555555, #444444, #0f0f0f);
}
.card_purple {
background: linear-gradient(to right bottom, #7a43df, #644897, #8964cf, #633cac, #512c96);
}
.card__front {
padding: 18px;
-webkit-transform: rotateY(0);
-moz-transform: rotateY(0);
}
.card__back {
padding: 18px 0;
-webkit-transform: rotateY(-180deg);
-moz-transform: rotateY(-180deg);
}
.card__black_line {
margin-top: 5px;
height: 38px;
background-color: #303030;
}
.card__logo {
height: 16px !important;
}
.card__front_chip {
left: 1.2rem;
height: 1.5rem !important;
position: absolute;
}
.card__front_logo {
position: absolute;
top: 18px;
right: 18px;
}
.card__square {
border-radius: 5px;
height: 30px !important;
}
.card_number {
display: block;
width: 100%;
word-spacing: 4px;
font-size: 20px;
letter-spacing: 2px;
color: #fff;
text-align: center;
margin-bottom: 20px;
margin-top: 20px;
}
.card__space_75 {
width: 75%;
float: left;
}
.card__space_25 {
width: 25%;
float: left;
}
.card__label {
font-size: 10px;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.8);
letter-spacing: 1px;
}
.card__info {
margin-bottom: 0;
margin-top: 5px;
font-size: 16px;
line-height: 18px;
color: #fff;
letter-spacing: 1px;
text-transform: uppercase;
}
.card__back_content {
padding: 15px 15px 0;
}
.card__secret__last {
color: #303030;
text-align: right;
margin: 0;
font-size: 14px;
}
.card__secret {
padding: 5px 12px;
background-color: #fff;
position: relative;
}
.card__secret:before {
content: '';
position: absolute;
top: -3px;
left: -3px;
height: calc(100% + 6px);
width: calc(100% - 42px);
border-radius: 4px;
background: repeating-linear-gradient(45deg, #ededed, #ededed 5px, #f9f9f9 5px, #f9f9f9 10px);
}
.card__back_logo {
position: absolute;
bottom: 15px;
right: 15px;
}
.card__back_square {
position: absolute;
bottom: 15px;
left: 15px;
}
.card:hover .card__front {
-webkit-transform: rotateY(180deg);
-moz-transform: rotateY(180deg);
}
.card:hover .card__back {
-webkit-transform: rotateY(0deg);
-moz-transform: rotateY(0deg);
}

View File

@@ -0,0 +1,117 @@
import {
IonCard,
IonCardContent,
IonCardSubtitle,
IonCardTitle,
IonCol,
IonGrid,
IonIcon,
IonNote,
IonRow,
IonSkeletonText,
IonText,
IonThumbnail,
} from '@ionic/react';
import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons';
export const SkeletonDashboard = () => (
<IonGrid>
<IonCard>
<IonCardContent className="ion-text-center">
<IonText color="primary">
<h1>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</h1>
</IonText>
<div className="ion-margin-top">
<IonThumbnail>
<IonSkeletonText animated style={{ width: '2rem', height: '2rem' }} />
</IonThumbnail>
<IonText color="dark">
<h1 style={{ fontWeight: 'bold' }}>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</h1>
</IonText>
<IonText color="medium">
<p>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</p>
</IonText>
</div>
<IonCardTitle style={{ fontSize: '3rem' }} className="ion-margin-top">
<IonSkeletonText animated style={{ height: '3rem', width: '30%', textAlign: 'center' }} />
</IonCardTitle>
<IonGrid className="ion-margin-top">
<IonRow>
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
<img alt="wind" src="/assets/WeatherDemo/wind.png" height="32" width="32" />
</IonCol>
<IonCol size="9">
<IonCardSubtitle>Wind</IonCardSubtitle>
<IonNote>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</IonNote>
</IonCol>
</IonRow>
</IonCol>
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
<IonIcon icon={thermometerOutline} color="medium" style={{ fontSize: '2rem' }} />
</IonCol>
<IonCol size="9">
<IonCardSubtitle>Feels like</IonCardSubtitle>
<IonNote>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</IonNote>
</IonCol>
</IonRow>
</IonCol>
</IonRow>
<IonRow className="ion-margin-top">
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
<IonIcon icon={sunnyOutline} color="medium" style={{ fontSize: '2rem' }} />
</IonCol>
<IonCol size="9">
<IonCardSubtitle>Index UV</IonCardSubtitle>
<IonNote>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</IonNote>
</IonCol>
</IonRow>
</IonCol>
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
<IonIcon icon={pulseOutline} color="medium" style={{ fontSize: '2rem' }} />
</IonCol>
<IonCol size="9">
<IonCardSubtitle>Pressure</IonCardSubtitle>
<IonNote>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</IonNote>
</IonCol>
</IonRow>
</IonCol>
</IonRow>
</IonGrid>
</IonCardContent>
</IonCard>
</IonGrid>
);

View File

@@ -0,0 +1,56 @@
import { IonAvatar, IonItem, IonLabel } from '@ionic/react';
import { formatBalance } from '../data/Utils';
import styles from './TransactionItem.module.css';
const TransactionItem = (props) => {
const { name, amount, deposit, color } = props;
const getContactNameInitials = (contactName) => {
var nameInitials = '';
if (contactName && contactName !== '' && contactName !== undefined) {
const nameParts = contactName && contactName.split(' ');
if (nameParts) {
if (nameParts[0].charAt(0).match(/^[a-z]+$/i)) {
nameInitials += nameParts[0].charAt(0).toUpperCase();
}
if (nameParts[1]) {
if (nameParts[1].charAt(0).match(/^[a-z]+$/i)) {
nameInitials += nameParts[1].charAt(0).toUpperCase();
}
} else {
nameInitials += nameParts[0].charAt(1).toUpperCase();
}
}
}
return nameInitials;
};
return (
<IonItem lines="full" detail={false} className={`item-text-wrap ion-text-wrap ${styles.transactionItem}`}>
<div className={styles.transactionItemContent}>
<IonAvatar slot="start">
<div style={{ borderColor: 'grey', color: 'grey' }} className={styles.avatarImage}>
{getContactNameInitials(name)}
</div>
</IonAvatar>
<IonLabel className={`ion-text-wrap ${styles.transactionContent}`}>
<h2>{name}</h2>
</IonLabel>
<IonLabel className={`ion-text-wrap ${styles.transactionContent}`}>
<h4 className={deposit ? styles.green : styles.red}>
{deposit ? '+' : '-'}
&pound;{formatBalance(amount)}
</h4>
</IonLabel>
</div>
</IonItem>
);
};
export default TransactionItem;

View File

@@ -0,0 +1,48 @@
.avatarImage {
/* background-color: var(--ion-color); */
width: 2.5rem;
height: 2.5rem;
border-radius: 500px;
color: black;
font-size: 1.3rem;
display: flex;
flex-direction: row;
justify-content: center;
align-content: center;
align-items: center;
padding: 0.5rem !important;
border: 2px solid rgb(44, 44, 44);
margin-top: 0.2rem;
}
.transactionItem {
flex-direction: row;
padding: 0;
margin: 0;
}
.transactionItemContent {
padding-left: 3rem;
padding-right: 2rem;
display: flex !important;
flex-direction: row !important;
justify-content: space-between;
width: 100%;
align-content: center;
align-items: center;
margin-top: -0.2rem;
margin-bottom: -0.2rem;
}
.transactionContent {
padding: 1rem;
text-align: left !important;
}
.green {
color: rgb(0, 165, 0);
}
.red {
color: red;
}

View File

@@ -0,0 +1,119 @@
import { Store } from 'pullstate';
export const AccountStore = new Store({
profile: {
firstname: 'Alan',
surname: 'Montgomery',
avatar: '/assets/DemoBankingUi/alan.jpg',
},
cards: [
{
id: 1,
type: 'visa',
description: 'Current Account',
number: '4859 2390 5635 7347',
expiry: '11/22',
secret: '483',
color: 'orange',
balance: '38.21',
transactions: [
{
name: 'Joe Bloggs',
amount: '2.50',
deposit: true,
},
{
name: 'Ocean Pratt',
amount: '12.99',
deposit: true,
},
{
name: 'Eugene Piper',
amount: '74.99',
deposit: false,
},
{
name: 'Emeli Potts',
amount: '4.20',
deposit: false,
},
{
name: 'Asia Wells',
amount: '12.73',
deposit: true,
},
{
name: 'Awais Brook',
amount: '17.10',
deposit: false,
},
{
name: 'Coen Haas',
amount: '9.99',
deposit: true,
},
],
},
{
id: 2,
type: 'visa',
description: 'Savings',
number: '7349 1284 6790 4587',
expiry: '05/23',
secret: '590',
color: 'blue',
balance: '120.90',
transactions: [
{
name: 'Joe Bloggs',
amount: '120.90',
deposit: true,
},
],
},
{
id: 3,
type: 'visa',
description: 'House Fund',
number: '6783 5692 4475 6682',
expiry: '01/24',
secret: '321',
color: 'purple',
balance: '0',
transactions: [],
},
],
});
export const addCardToAccount = (newCard) => {
AccountStore.update((s) => {
s.cards = [...s.cards, newCard];
});
};
export const addTransactionToCard = (newTransaction, cardID) => {
AccountStore.update((s) => {
s.cards.find((c, index) =>
parseInt(c.id) === parseInt(cardID) ? (s.cards[index].transactions = [...s.cards[index].transactions, newTransaction]) : false
);
});
if (newTransaction.deposit) {
AccountStore.update((s) => {
s.cards.find((c, index) =>
parseInt(c.id) === parseInt(cardID) ? (s.cards[index].balance = parseFloat(s.cards[index].balance) + parseFloat(newTransaction.amount)) : false
);
});
} else {
AccountStore.update((s) => {
s.cards.find((c, index) =>
parseInt(c.id) === parseInt(cardID) ? (s.cards[index].balance = parseFloat(s.cards[index].balance) - parseFloat(newTransaction.amount)) : false
);
});
}
};
// export const removeFromCart = productIndex => {
// AccountStore.update(s => { s.product_ids.splice(productIndex, 1) });
// }

View File

@@ -0,0 +1,6 @@
import { Store } from 'pullstate';
export const CardStore = new Store({
card_colors: ['orange', 'black', 'blue', 'purple'],
card_types: ['visa', 'mastercard'],
});

View File

@@ -0,0 +1,9 @@
export const formatBalance = (balance) => {
var formatter = new Intl.NumberFormat('en-GB', {
// style: 'currency',
currency: 'GBP',
minimumFractionDigits: 2,
});
return formatter.format(balance);
};

View File

@@ -0,0 +1,42 @@
import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
import { cloudOutline, searchOutline } from 'ionicons/icons';
import { Route, Redirect } from 'react-router';
import Tab1 from './AppPages/Tab1';
import Tab2 from './AppPages/Tab2';
import Home from './pages/Home.jsx';
import Account from './pages/Account';
import AddCard from './pages/AddCard';
import AddTransaction from './pages/AddTransaction';
import './style.scss';
function DemoBankingUi() {
return (
<IonTabs className="demo-banking-ui">
<IonRouterOutlet>
<Route path="/demo-banking-ui/home" exact={true}>
<Home />
</Route>
<Route path="/demo-banking-ui/account" exact={true}>
<Account />
</Route>
<Route path="/demo-banking-ui/account/add-card" exact={true}>
<AddCard />
</Route>
<Route path="/demo-banking-ui/add-transaction/:card_id" exact={true}>
<AddTransaction />
</Route>
<Redirect exact path="/demo-banking-ui" to="/demo-banking-ui/home" />
</IonRouterOutlet>
</IonTabs>
);
}
export default DemoBankingUi;

View File

@@ -0,0 +1,95 @@
import {
IonBackButton,
IonButton,
IonButtons,
IonCol,
IonContent,
IonGrid,
IonHeader,
IonIcon,
IonItem,
IonLabel,
IonPage,
IonRow,
IonTitle,
IonToolbar,
} from '@ionic/react';
import styles from './Account.module.css';
import { AccountStore } from '../data/AccountStore';
import { addOutline, logOutOutline } from 'ionicons/icons';
import { formatBalance } from '../data/Utils';
const Account = () => {
const cards = AccountStore.useState((s) => s.cards);
const profile = AccountStore.useState((s) => s.profile);
return (
<IonPage className={styles.accountPage}>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonBackButton color="dark" />
</IonButtons>
<IonTitle>Account</IonTitle>
<IonButtons slot="end">
<IonButton>
<IonIcon color="dark" icon={logOutOutline} />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonGrid>
<IonRow className="ion-text-center ion-justify-content-center">
<IonCol size="4" className="animate__animated animate__fadeInTopLeft animate__faster">
<img src={profile.avatar} className={styles.avatar} alt="account avatar" />
</IonCol>
</IonRow>
<IonRow className={`ion-no-margin ion-text-center ion-justify-content-center ${styles.profileDetails}`}>
<IonCol size="12">
<h5>{`${profile.firstname} ${profile.surname}`}</h5>
<h6>{cards.length} current cards</h6>
</IonCol>
</IonRow>
<IonRow className="ion-text-center">
<IonCol size="12">
<IonButton color="primary" routerLink="/account/add-card" routerDirection="forward">
<IonIcon icon={addOutline} />
Add Card
</IonButton>
</IonCol>
</IonRow>
<div className="ion-margin-top">
{cards.map((card, index) => {
return (
<IonRow key={`smallCard_${index}`} className="animate__animated animate__fadeInLeft animate__faster">
<IonCol size="12">
<IonItem className={styles.cardItem} detail={false} lines="none">
<div className={styles.smallCard} style={{ backgroundColor: card.color }}></div>
<IonLabel className={`ion-text-left ${styles.cardDescription}`}>
<h4>{card.description}</h4>
</IonLabel>
<IonLabel className="ion-text-right">
<h4>&pound;{formatBalance(card.balance)}</h4>
</IonLabel>
</IonItem>
</IonCol>
</IonRow>
);
})}
</div>
</IonGrid>
</IonContent>
</IonPage>
);
};
export default Account;

View File

@@ -0,0 +1,54 @@
.accountPage ion-toolbar {
--border-style: none;
--padding-top: 1rem;
--padding-bottom: 1rem;
padding-left: 1rem;
padding-right: 1rem;
}
.avatar {
border-radius: 500px;
border: 3px solid var(--ion-color-primary);
padding: 0.2rem;
}
.profileDetails {
}
.profileDetails h6,
.profileDetails h5 {
margin: 0;
padding: 0;
}
.profileDetails h6 {
color: var(--ion-color-medium);
}
.cards {
/* margin-top: */
}
.smallCard {
width: 15%;
height: 80%;
border-radius: 5px;
opacity: 0.7;
}
.cardDescription {
padding-left: 1.5rem;
}
.cardDescription h4 {
font-weight: 400;
}
.cardItem {
--background: rgb(246, 246, 246);
--border-radius: 5px;
--padding-start: 1rem;
--padding-end: 1rem;
--padding-top: 0.5rem;
--padding-bottom: 0.5rem;
}

View File

@@ -0,0 +1,209 @@
import { useState } from 'react';
import {
IonBackButton,
IonButton,
IonButtons,
IonCol,
IonContent,
IonGrid,
IonHeader,
IonIcon,
IonInput,
IonItem,
IonLabel,
IonPage,
IonRow,
IonSelect,
IonSelectOption,
IonTitle,
IonToolbar,
} from '@ionic/react';
import styles from './Account.module.css';
import DebitCard from '../components/DebitCard';
import { AccountStore, addCardToAccount } from '../data/AccountStore';
import { CardStore } from '../data/CardStore';
import { addOutline, timerOutline } from 'ionicons/icons';
import { useHistory } from 'react-router';
const AddCard = () => {
const cards = AccountStore.useState((s) => s.cards);
const cardTypes = CardStore.useState((s) => s.card_types);
const cardColors = CardStore.useState((s) => s.card_colors);
const profile = AccountStore.useState((s) => s.profile);
const [cardType, setCardType] = useState(cardTypes[0]);
const [cardColor, setCardColor] = useState(cardColors[0]);
const [cardDescription, setCardDescription] = useState('');
const [cardNumber, setCardNumber] = useState('1234 1234 1234 1234');
const [cardSecret, setCardSecret] = useState('123');
const [cardExpiry, setCardExpiry] = useState('01/22');
const [cardBalance, setCardBalance] = useState(0);
const history = useHistory();
const [adding, setAdding] = useState(false);
const addCard = async () => {
setAdding(true);
const newCard = {
id: cards.length + 1,
type: cardType,
color: cardColor,
description: cardDescription,
number: cardNumber,
secret: cardSecret,
expiry: cardExpiry,
balance: cardBalance,
transactions: [
{
name: 'Starting Balance',
amount: cardBalance,
deposit: true,
},
],
};
await addCardToAccount(newCard);
setTimeout(() => {
setAdding(false);
history.goBack();
}, 500);
};
return (
<IonPage className={styles.accountPage}>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonBackButton color="dark" />
</IonButtons>
<IonTitle>Add Card</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonGrid>
<IonRow className="animate__animated animate__fadeInTopLeft animate__faster ion-justify-content-center ion-text-center">
<IonCol size="12" className="ion-justify-content-center ion-text-center">
<DebitCard color={cardColor} type={cardType} expiry={cardExpiry} number={cardNumber} secret={cardSecret} profile={profile} />
</IonCol>
</IonRow>
<IonRow className="ion-padding-top">
<IonCol size="6">
<IonItem lines="full">
<IonLabel position="floating">Card Type</IonLabel>
<IonSelect placeholder="Select type" value={cardType} onIonChange={(e) => setCardType(e.currentTarget.value)}>
{cardTypes.map((option, index) => {
return (
<IonSelectOption key={index} value={option}>
{option.toUpperCase()}
</IonSelectOption>
);
})}
</IonSelect>
</IonItem>
</IonCol>
<IonCol size="6">
<IonItem lines="full">
<IonLabel position="floating">Card Color</IonLabel>
<IonSelect placeholder="Select color" value={cardColor} onIonChange={(e) => setCardColor(e.currentTarget.value)}>
{cardColors.map((option, index) => {
return (
<IonSelectOption key={index} value={option}>
{option.toUpperCase()}
</IonSelectOption>
);
})}
</IonSelect>
</IonItem>
</IonCol>
</IonRow>
<IonRow>
<IonCol size="6">
<IonItem lines="full">
<IonLabel position="floating">Card Name</IonLabel>
<IonInput
type="text"
inputmode="text"
placeholder="Card name"
value={cardDescription}
onIonChange={(e) => setCardDescription(e.currentTarget.value)}
/>
</IonItem>
</IonCol>
<IonCol size="6">
<IonItem lines="full">
<IonLabel position="floating">Starting Balance</IonLabel>
<IonInput type="text" inputmode="text" placeholder="0" value={cardBalance} onIonChange={(e) => setCardBalance(e.currentTarget.value)} />
</IonItem>
</IonCol>
</IonRow>
<IonRow>
<IonCol size="12">
<IonItem lines="full">
<IonLabel position="floating">Card Number</IonLabel>
<IonInput
type="text"
inputmode="text"
placeholder="**** **** **** ****"
value={cardNumber}
onIonChange={(e) => setCardNumber(e.currentTarget.value)}
/>
</IonItem>
</IonCol>
</IonRow>
<IonRow>
<IonCol size="6">
<IonItem lines="full">
<IonLabel position="floating">Card Expiry</IonLabel>
<IonInput type="text" inputmode="text" placeholder="01/22" value={cardExpiry} onIonChange={(e) => setCardExpiry(e.currentTarget.value)} />
</IonItem>
</IonCol>
<IonCol size="6">
<IonItem lines="full">
<IonLabel position="floating">Card Secret</IonLabel>
<IonInput type="text" inputmode="text" placeholder="123" value={cardSecret} onIonChange={(e) => setCardSecret(e.currentTarget.value)} />
</IonItem>
</IonCol>
</IonRow>
<IonRow>
<IonCol size="12">
<IonButton
style={{ '--background': cardColor, '--background-focused': cardColor, '--background-hover': cardColor, '--background-activated': cardColor }}
expand="block"
disabled={adding}
onClick={addCard}
>
{!adding && (
<>
<IonIcon icon={addOutline} />
&nbsp; Add Card
</>
)}
{adding && (
<>
<IonIcon icon={timerOutline} />
&nbsp; Adding...
</>
)}
</IonButton>
</IonCol>
</IonRow>
</IonGrid>
</IonContent>
</IonPage>
);
};
export default AddCard;

View File

@@ -0,0 +1,165 @@
import { useState } from 'react';
import {
IonBackButton,
IonButton,
IonButtons,
IonCol,
IonContent,
IonGrid,
IonHeader,
IonIcon,
IonInput,
IonItem,
IonLabel,
IonPage,
IonRow,
IonSelect,
IonSelectOption,
IonTitle,
IonToggle,
IonToolbar,
useIonViewWillEnter,
} from '@ionic/react';
import styles from './Account.module.css';
import DebitCard from '../components/DebitCard';
import { AccountStore, addCardToAccount, addTransactionToCard } from '../data/AccountStore';
import { CardStore } from '../data/CardStore';
import { addOutline, timerOutline } from 'ionicons/icons';
import { useHistory, useParams } from 'react-router';
const AddTransaction = () => {
const cards = AccountStore.useState((s) => s.cards);
const profile = AccountStore.useState((s) => s.profile);
const [cardID, setCardID] = useState(false);
const [card, setCard] = useState({});
const [transactionName, setTransactionName] = useState('Test Transaction');
const [transactionAmount, setTransactionAmount] = useState(0);
const [transactionDeposit, setTransactionDeposit] = useState(false);
const history = useHistory();
const params = useParams();
const [adding, setAdding] = useState(false);
useIonViewWillEnter(() => {
const tempCardID = params.card_id;
const tempCard = cards.filter((c) => parseInt(c.id) === parseInt(tempCardID))[0];
setCardID(tempCardID);
setCard(tempCard);
});
const addTransaction = async () => {
setAdding(true);
const newTransaction = {
name: transactionName,
amount: transactionAmount,
deposit: transactionDeposit,
};
await addTransactionToCard(newTransaction, cardID);
setTimeout(() => {
setAdding(false);
history.goBack();
}, 500);
};
return (
<IonPage className={styles.accountPage}>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonBackButton color="dark" />
</IonButtons>
<IonTitle>Add Transaction</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonGrid>
<IonRow className="animate__animated animate__fadeInTopLeft animate__faster ion-justify-content-center ion-text-center">
<IonCol size="12" className="ion-justify-content-center ion-text-center">
<DebitCard {...card} profile={profile} />
</IonCol>
</IonRow>
<IonRow>
<IonCol size="6">
<IonItem lines="full">
<IonLabel position="floating">Name</IonLabel>
<IonInput
type="text"
inputmode="text"
placeholder="Transaction name"
value={transactionName}
onIonChange={(e) => setTransactionName(e.currentTarget.value)}
/>
</IonItem>
</IonCol>
<IonCol size="6">
<IonItem lines="full">
<IonLabel position="floating">Amount</IonLabel>
<IonInput
type="text"
inputmode="text"
placeholder="0"
value={transactionAmount}
onIonChange={(e) => setTransactionAmount(e.currentTarget.value)}
/>
</IonItem>
</IonCol>
</IonRow>
<IonRow>
<IonCol size="12">
<IonItem lines="full">
<IonLabel>Deposit?</IonLabel>
<IonToggle
style={{ '--background-checked': card.color }}
slot="end"
value={transactionDeposit}
onIonChange={(e) => setTransactionDeposit(e.currentTarget.checked)}
/>
</IonItem>
</IonCol>
</IonRow>
<IonRow>
<IonCol size="12">
<IonButton
style={{
'--background': card.color,
'--background-focused': card.color,
'--background-hover': card.color,
'--background-activated': card.color,
}}
expand="block"
disabled={adding}
onClick={addTransaction}
>
{!adding && (
<>
<IonIcon icon={addOutline} />
&nbsp; Add Transaction
</>
)}
{adding && (
<>
<IonIcon icon={timerOutline} />
&nbsp; Adding...
</>
)}
</IonButton>
</IonCol>
</IonRow>
</IonGrid>
</IonContent>
</IonPage>
);
};
export default AddTransaction;

View File

@@ -0,0 +1,116 @@
import { useRef, useState } from 'react';
import { IonButton, IonButtons, IonContent, IonGrid, IonHeader, IonIcon, IonPage, IonTitle, IonToolbar, useIonRouter, useIonViewDidEnter } from '@ionic/react';
import styles from './Home.module.css';
import { AccountStore } from '../data/AccountStore';
import CardSlide from '../components/CardSlide';
import { chevronBackOutline, searchOutline } from 'ionicons/icons';
// Import Swiper React components
import { Swiper, SwiperSlide } from 'swiper/react';
// Import Swiper styles
// import 'swiper/swiper.scss';
import 'swiper/css';
import stylesS from './Home.module.scss';
const Home = () => {
const cards = AccountStore.useState((s) => s.cards);
const profile = AccountStore.useState((s) => s.profile);
const [pageTitle, setPageTitle] = useState(cards[0].description);
const [mainColor, setMainColor] = useState(cards[0].color);
const [slideSpace, setSlideSpace] = useState(10);
const slidesRef = useRef();
useIonViewDidEnter(() => {
setSlideSpace(0);
});
const changeSlide = async (e) => {
const swiper = e;
const swiperIndex = swiper.activeIndex;
setPageTitle(cards[swiperIndex].description);
setMainColor(cards[swiperIndex].color);
document.getElementById(`slide_${swiperIndex}_balance`).classList.add('animate__headShake');
setTimeout(() => {
document.getElementById(`slide_${swiperIndex}_balance`).classList.remove('animate__headShake');
}, 1000);
};
const manageTouch = async (touched, e) => {
const swiper = e;
const swiperIndex = swiper.activeIndex;
if (touched) {
document.getElementById(`slide_${swiperIndex}_transactions`).classList.add('animate__fadeOut');
} else {
document.getElementById(`slide_${swiperIndex}_transactions`).classList.remove('animate__fadeOut');
document.getElementById(`slide_${swiperIndex}_transactions`).classList.add('animate__fadeIn');
}
};
const router = useIonRouter();
function handleBackClick() {
router.goBack();
}
return (
<IonPage className={stylesS.homePage}>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
{/* */}
<IonButton
//
routerLink="/demo-banking-ui/account"
className={stylesS.toolbarAvatar}
>
<img alt="toolbar avatar" className={stylesS.toolbarAvatarImage} src={profile.avatar} />
</IonButton>
</IonButtons>
<IonTitle>{pageTitle}</IonTitle>
<IonButtons slot="end">
<IonButton onClick={() => handleBackClick()}>
<IonIcon icon={chevronBackOutline} color="primary" />
</IonButton>
{/* */}
<IonButton>
<IonIcon color="light" icon={searchOutline} style={{ backgroundColor: mainColor, borderRadius: '500px', padding: '0.2rem' }} />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonGrid>
<Swiper
spaceBetween={slideSpace}
ref={slidesRef}
slidesPerView={1}
className={stylesS.cardsContainer}
onTouchStart={(e) => manageTouch(true, e)}
onTouchEnd={(e) => manageTouch(false, e)}
onSlideChange={(e) => changeSlide(e)}
>
{cards.map((card, index) => {
return (
<SwiperSlide key={`slide_${index}`} id={`slide_${index}`} className={stylesS.customSlide}>
<CardSlide key={index} card={card} profile={profile} index={index} />
</SwiperSlide>
);
})}
</Swiper>
</IonGrid>
</IonContent>
</IonPage>
);
};
export default Home;

View File

@@ -0,0 +1,38 @@
.homePage ion-toolbar {
--border-style: none;
--padding-top: 1rem;
--padding-bottom: 1rem;
padding-left: 1rem;
padding-right: 1rem;
}
.customSlide {
display: flex;
flex-direction: column;
}
.transactionList {
overflow: scroll;
width: 100vw;
}
.balance {
font-weight: 300;
font-size: 1.5rem;
color: black;
}
.poundSign {
font-weight: 800;
font-size: 1.2rem;
}
.toolbarAvatarImage {
border-radius: 500px;
height: 100%;
width: auto;
}
.helloworld {
background-color: gold;
}

View File

@@ -0,0 +1,38 @@
.homePage ion-toolbar {
--border-style: none;
--padding-top: 1rem;
--padding-bottom: 1rem;
padding-left: 1rem;
padding-right: 1rem;
}
.customSlide {
display: flex;
flex-direction: column;
}
.transactionList {
overflow: scroll;
width: 100vw;
}
.balance {
font-weight: 300;
font-size: 1.5rem;
color: black;
}
.poundSign {
font-weight: 800;
font-size: 1.2rem;
}
.toolbarAvatarImage {
border-radius: 500px;
height: 32px;
width: auto;
}
.helloworld {
background-color: cyan;
}

View File

@@ -0,0 +1,139 @@
.demo-banking-ui {
/* Ionic Variables and Theming. For more info, please see:
http://ionicframework.com/docs/theming/ */
/** Ionic CSS Variables **/
* {
/** primary **/
--ion-color-primary: #3880ff;
--ion-color-primary-rgb: 56, 128, 255;
--ion-color-primary-contrast: #ffffff;
--ion-color-primary-contrast-rgb: 255, 255, 255;
--ion-color-primary-shade: #3171e0;
--ion-color-primary-tint: #4c8dff;
/** secondary **/
--ion-color-secondary: #3dc2ff;
--ion-color-secondary-rgb: 61, 194, 255;
--ion-color-secondary-contrast: #ffffff;
--ion-color-secondary-contrast-rgb: 255, 255, 255;
--ion-color-secondary-shade: #36abe0;
--ion-color-secondary-tint: #50c8ff;
/** tertiary **/
--ion-color-tertiary: #5260ff;
--ion-color-tertiary-rgb: 82, 96, 255;
--ion-color-tertiary-contrast: #ffffff;
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
--ion-color-tertiary-shade: #4854e0;
--ion-color-tertiary-tint: #6370ff;
/** success **/
--ion-color-success: #2dd36f;
--ion-color-success-rgb: 45, 211, 111;
--ion-color-success-contrast: #ffffff;
--ion-color-success-contrast-rgb: 255, 255, 255;
--ion-color-success-shade: #28ba62;
--ion-color-success-tint: #42d77d;
/** warning **/
--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;
/** danger **/
--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;
/** dark **/
--ion-color-dark: #222428;
--ion-color-dark-rgb: 34, 36, 40;
--ion-color-dark-contrast: #ffffff;
--ion-color-dark-contrast-rgb: 255, 255, 255;
--ion-color-dark-shade: #1e2023;
--ion-color-dark-tint: #383a3e;
/** medium **/
--ion-color-medium: #92949c;
--ion-color-medium-rgb: 146, 148, 156;
--ion-color-medium-contrast: #ffffff;
--ion-color-medium-contrast-rgb: 255, 255, 255;
--ion-color-medium-shade: #808289;
--ion-color-medium-tint: #9d9fa6;
/** light **/
--ion-color-light: #f4f5f8;
--ion-color-light-rgb: 244, 245, 248;
--ion-color-light-contrast: #000000;
--ion-color-light-contrast-rgb: 0, 0, 0;
--ion-color-light-shade: #d7d8da;
--ion-color-light-tint: #f5f6f9;
}
ion-item {
--padding-start: 0;
--inner-padding-end: 0;
}
ion-label {
margin-top: 12px;
margin-bottom: 12px;
}
ion-item h2 {
font-weight: 600;
margin: 0;
}
ion-item p {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
width: 95%;
}
ion-item .date {
float: right;
align-items: center;
display: flex;
}
ion-item ion-icon {
color: #c9c9ca;
}
ion-item ion-note {
font-size: 15px;
margin-right: 8px;
font-weight: normal;
}
ion-item ion-note.md {
margin-right: 14px;
}
.dot {
display: block;
height: 12px;
width: 12px;
border-radius: 50%;
align-self: start;
margin: 16px 10px 16px 16px;
}
.dot-unread {
background: var(--ion-color-primary);
}
ion-footer ion-title {
font-size: 11px;
font-weight: normal;
}
}

View File

@@ -0,0 +1,7 @@
.view-post-footer {
background-color: white;
padding-left: 1rem;
padding-right: 1rem;
padding-bottom: 1rem;
}

View File

@@ -0,0 +1,90 @@
import {
IonBackButton,
IonBadge,
IonButton,
IonButtons,
IonCardSubtitle,
IonCardTitle,
IonCol,
IonContent,
IonFooter,
IonGrid,
IonHeader,
IonIcon,
IonNote,
IonPage,
IonRow,
IonText,
IonTitle,
IonToolbar,
} from '@ionic/react';
import { bookmarkOutline, shareOutline } from 'ionicons/icons';
import { useParams } from 'react-router';
import { blogPosts } from '../localData';
import './BlogPost.css';
const BlogPost = () => {
const { id } = useParams();
const post = blogPosts.filter((post) => parseInt(post.id) === parseInt(id))[0];
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Blog</IonTitle>
<IonButtons slot="start">
<IonBackButton text="Blog Posts" />
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<img src={post.image} alt="post header" />
<IonGrid className="ion-padding-start ion-padding-end">
<IonRow className="ion-align-items-center ion-justify-content-between">
<IonRow className="ion-align-items-center ion-justify-content-between">
<img src={post.authorImage} className="post-author-avatar" alt="post author" />
<IonCardSubtitle className="ion-no-margin ion-no-padding ion-margin-start">
{post.author}
</IonCardSubtitle>
</IonRow>
<IonNote>{post.date}</IonNote>
</IonRow>
<IonRow>
<IonCol size="12">
<IonCardTitle className="post-title">{post.title}</IonCardTitle>
</IonCol>
</IonRow>
<IonRow>
<IonCol size="12">
<IonText color="medium">{post.content}</IonText>
</IonCol>
</IonRow>
</IonGrid>
</IonContent>
<IonFooter className="view-post-footer">
<IonRow className="post-footer ion-align-self-center ion-justify-content-between">
<div>
<IonButton fill="clear" color="primary">
<IonIcon icon={shareOutline} />
</IonButton>
<IonButton fill="clear" color="primary">
<IonIcon icon={bookmarkOutline} />
</IonButton>
</div>
<div>
<IonBadge color="primary" className="post-category">
{post.category}
</IonBadge>
</div>
</IonRow>
</IonFooter>
</IonPage>
);
};
export default BlogPost;

View File

@@ -0,0 +1,51 @@
import {
IonButton,
IonButtons,
IonContent,
IonHeader,
IonIcon,
IonPage,
IonTitle,
IonToolbar,
useIonRouter,
} from '@ionic/react';
import { Post } from '../components/Post';
import { blogPosts } from '../localData';
import { chevronBackOutline } from 'ionicons/icons';
const Home = () => {
const router = useIonRouter();
function handleBackClick() {
router.goBack();
}
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Ionic Blog</IonTitle>
<IonButtons slot="start">
<IonButton onClick={() => handleBackClick()}>
<IonIcon icon={chevronBackOutline} color="primary" />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Ionic Blog</IonTitle>
</IonToolbar>
</IonHeader>
{blogPosts.map((post, index) => (
<Post post={post} key={`post_${index}`} />
))}
</IonContent>
</IonPage>
);
};
export default Home;

View File

@@ -0,0 +1,37 @@
.post-author-avatar {
height: 2rem;
width: 2rem;
border-radius: 500px;
}
.post-title {
font-size: 1.4rem;
margin-top: 0.75rem;
}
.post-content {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}
.post-footer {
display: flex;
flex-direction: row;
justify-content: space-between;
align-content: center;
width: 100%;
border-top: 2px solid rgb(245, 245, 245);
margin-top: 2rem;
padding-top: 1rem;
}
.post-category {
margin-top: 1.1rem;
}
.post-image {
width: 100%;
}

View File

@@ -0,0 +1,57 @@
import {
IonBadge,
IonButton,
IonCard,
IonCardContent,
IonCardHeader,
IonCardSubtitle,
IonCardTitle,
IonIcon,
IonNote,
IonRow,
} from '@ionic/react';
import { bookmarkOutline, shareOutline } from 'ionicons/icons';
import './Post.css';
export const Post = ({ post }) => {
return (
<IonCard routerLink={`/demo-blog-post-ui/post/${post.id}`}>
<img src={post.image} alt="main post" className="post-image" />
<IonCardHeader>
<IonRow className="ion-align-items-center ion-justify-content-between">
<IonRow className="ion-align-items-center ion-justify-content-between">
<img src={post.authorImage} className="post-author-avatar" alt="post author" />
<IonCardSubtitle className="ion-no-margin ion-no-padding ion-margin-start">
{post.author}
</IonCardSubtitle>
</IonRow>
<IonNote>{post.date}</IonNote>
</IonRow>
<IonCardTitle className="post-title">{post.title}</IonCardTitle>
</IonCardHeader>
<IonCardContent>
<p className="post-content">{post.content}</p>
<IonRow className="post-footer ion-align-self-center ion-justify-content-between">
<div>
<IonButton fill="clear" color="primary">
<IonIcon icon={shareOutline} />
</IonButton>
<IonButton fill="clear" color="primary">
<IonIcon icon={bookmarkOutline} />
</IonButton>
</div>
<div>
<IonBadge color="primary" className="post-category">
{post.category}
</IonBadge>
</div>
</IonRow>
</IonCardContent>
</IonCard>
);
};

View File

@@ -0,0 +1,27 @@
import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
import { cloudOutline, searchOutline } from 'ionicons/icons';
import { Route, Redirect } from 'react-router';
import Home from './AppPages/Home';
import BlogPost from './AppPages/BlogPost';
import './style.scss';
function DemoBlogPostUi() {
return (
<IonRouterOutlet className="demo-blog-post-ui">
<Route exact path="/demo-blog-post-ui/home">
<Home />
</Route>
<Route exact path="/demo-blog-post-ui/post/:id">
<BlogPost />
</Route>
<Redirect exact path="/demo-blog-post-ui" to="/demo-blog-post-ui/home" />
</IonRouterOutlet>
);
}
export default DemoBlogPostUi;

View File

@@ -0,0 +1,98 @@
export const blogPosts = [
{
"id": 1,
"title": "How to Convince Your Boss to Choose Ionic",
"title_link": "https://ionicframework.com/blog/convince-boss-choose-ionic-app-development/",
"date": "August 3, 2021",
"author": "By Kim Maida",
"authorImage": "https://ionicframework.com/blog/wp-content/uploads/2021/07/kim-maida-150x150.jpg",
"category": "ANNOUNCEMENTS",
"category_link": "https://ionicframework.com/blog//blog/category/announcements",
"image": "https://ionicframework.com/blog/wp-content/uploads/2021/07/how-to-convince-your-boss_image_1aug2021.png",
"content": "Greetings, friend! Youre a web developer, team lead, or engineering manager who has discovered that Ionic products are awesome. They have helped you build cross-platform applications quickly, made the app development process enjoyable, and solved important mobile development problems. You can see that Ionic would be extremely beneficial in your daily job, but are wondering how to convince your boss to endorse the adoption of new software. In a nutshell:"
},
{
"id": 2,
"title": "Ioniconf 2021 Conference Recap",
"title_link": "https://ionicframework.com/blog/ioniconf-2021-conference-recap/",
"date": "July 29, 2021",
"author": "By Mike Hartington",
"authorImage": "https://ionicframework.com/blog/wp-content/uploads/2018/08/mike-headshot-2-smaller-150x150.png",
"category": "ANNOUNCEMENTS",
"category_link": "https://ionicframework.com/blog//blog/category/announcements",
"image": "https://ionicframework.com/blog/wp-content/uploads/2021/06/og-imgx2.png",
"content": "And with that, Ioniconf 2021 has concluded! Ioniconf, our online conference for Ionic developers and the wider web development community, featured twelve expert Ionic speakers and was attended by many thousands of Ionic community members. Were thrilled by the communitys reception to the event and are already looking forward to our next event taking place in September. Read on for a recap and links to all recorded talks."
},
{
"id": 3,
"title": "Announcing Identity Vault 5.0",
"title_link": "https://ionicframework.com/blog/announcing-identity-vault-5-0/",
"date": "July 28, 2021",
"author": "By Dallas James",
"authorImage": "https://ionicframework.com/blog/wp-content/uploads/2021/07/dallas-james-150x150.jpg",
"category": "PRODUCT",
"category_link": "https://ionicframework.com/blog//blog/category/announcements",
"image": "https://ionicframework.com/blog/wp-content/uploads/2021/07/iv-5-feature-image.png",
"content": "Today Im excited to announce Identity Vault 5.0, the newest version of Ionics mobile biometrics solution. Featuring the latest in native security best practices, Identity Vault improves frontend security in any Ionic app by making it easy to add secure biometric authentication in minutes."
},
{
"id": 4,
"title": "Building with Stencil: Clock Component",
"title_link": "https://ionicframework.com/blog/building-with-stencil-clock-component/",
"date": "July 22, 2021",
"author": "By Kevin Hoyt",
"authorImage": "https://ionicframework.com/blog/wp-content/uploads/2021/07/2520666-150x150.jpg",
"category": "ANNOUNCEMENTS",
"category_link": "https://ionicframework.com/blog//blog/category/announcements",
"image": "https://ionicframework.com/blog/wp-content/uploads/2021/07/Image-from-iOS.png",
"content": "I have not seen a clock in a web-based user interface in a long time. This makes sense — they are pretty redundant these days. You have a clock on your watch, on your mobile device, and on your desktop, and those are just the digital versions available at a glance. Nonetheless, the process of building a clock can reveal a lot about how a platform works."
},
{
"id": 5,
"title": "Building with Stencil: Calendar Component",
"title_link": "https://ionicframework.com/blog/building-with-stencil-calendar-component/",
"date": "July 19, 2021",
"author": "By Kevin Hoyt",
"authorImage": "https://ionicframework.com/blog/wp-content/uploads/2021/07/2520666-150x150.jpg",
"category": "TUTORIALS",
"category_link": null,
"image": "https://ionicframework.com/blog/wp-content/uploads/2021/07/ionic-blog-post-image_first-look-01.png",
"content": "Take a look at the month view of a calendar and you will see several rows of numbers. The numbers themselves, increasing in value one after the other, are arranged in columns. HTML and CSS provide us with a number of tools to display content in rows and columns. Making a calendar component should be easy, right? Right?"
},
{
"id": 6,
"title": "Introducing the New Overlay Hooks for Ionic React",
"title_link": "https://ionicframework.com/blog/introducing-the-new-overlay-hooks-for-ionic-react/",
"date": "July 14, 2021",
"author": "By Ely Lucas",
"authorImage": "https://secure.gravatar.com/avatar/45ad19965b4bde97e9f4396ea01ed184?s=32&r=g",
"category": "ENGINEERING",
"category_link": null,
"image": "https://ionicframework.com/blog/wp-content/uploads/2021/07/react-overlay-hooks-feature-image.png",
"content": "Hello Friends! We know everyone is excited about the new features in Ionic Framework 6.0 beta, but that doesnt mean were done with V5! In Ionic React 5.6, we packaged up a new set of hooks for controlling our overlay components that we think you might like. What is an overlay you ask? Its the term we give components that display over your current content, such as alerts, modals, toasts, etc."
},
{
"id": 7,
"title": "The Future of Stencil: Expanded Team, New Software Platform, and More",
"title_link": "https://ionicframework.com/blog/the-future-of-stencil-expanded-team-new-software-platform-and-more/",
"date": "July 7, 2021",
"author": "By Nick Hyatt",
"authorImage": "https://ionicframework.com/blog/wp-content/uploads/2018/11/Nick-Hyatt-Headshot-150x150.jpeg",
"category": "ANNOUNCEMENTS",
"category_link": null,
"image": "https://ionicframework.com/blog/wp-content/uploads/2021/07/stencil-future-feature-image.png",
"content": "Today Im excited to share some news about Stencil, Ionics open source toolchain that generates small, fast, and 100% standards-based Web Components that run in every browser. As you might have noticed, weve been actively increasing our investments across the entire Ionic App Platform, including the recent launch of Capacitor 3.0, Ionic Portals, tons of Appflow improvements, and the upcoming Ionic Framework v6."
},
{
"id": 8,
"title": "Announcing the Ionic Framework v6 Beta",
"title_link": "https://ionicframework.com/blog/announcing-the-ionic-framework-v6-beta/",
"date": "June 29, 2021",
"author": "By Liam DeBeasi",
"authorImage": "https://ionicframework.com/blog/wp-content/uploads/2020/01/ZNK4lRAJ_400x400-150x150.jpg",
"category": "ANNOUNCEMENTS",
"category_link": null,
"image": "https://ionicframework.com/blog/wp-content/uploads/2021/06/framework6-feature-image.png",
"content": "Earlier this week I had the privilege of giving the Ionic Framework Update at Ioniconf 2021 where we announced the Ionic Framework v6 beta. Ionic Framework has come far from its roots as an AngularJS-only UI library to a truly cross-platform framework for building Web Native applications. As we look to the future of Ionic Framework, lets talk about some of the improvements coming in Framework v6 and how you can get access to these improvements today."
}
];

View File

@@ -0,0 +1,79 @@
.demo-blog-post-ui {
/* Ionic Variables and Theming. For more info, please see:
http://ionicframework.com/docs/theming/ */
/** Ionic CSS Variables **/
* {
/** primary **/
--ion-color-primary: #3880ff;
--ion-color-primary-rgb: 56, 128, 255;
--ion-color-primary-contrast: #ffffff;
--ion-color-primary-contrast-rgb: 255, 255, 255;
--ion-color-primary-shade: #3171e0;
--ion-color-primary-tint: #4c8dff;
/** secondary **/
--ion-color-secondary: #3dc2ff;
--ion-color-secondary-rgb: 61, 194, 255;
--ion-color-secondary-contrast: #ffffff;
--ion-color-secondary-contrast-rgb: 255, 255, 255;
--ion-color-secondary-shade: #36abe0;
--ion-color-secondary-tint: #50c8ff;
/** tertiary **/
--ion-color-tertiary: #5260ff;
--ion-color-tertiary-rgb: 82, 96, 255;
--ion-color-tertiary-contrast: #ffffff;
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
--ion-color-tertiary-shade: #4854e0;
--ion-color-tertiary-tint: #6370ff;
/** success **/
--ion-color-success: #2dd36f;
--ion-color-success-rgb: 45, 211, 111;
--ion-color-success-contrast: #ffffff;
--ion-color-success-contrast-rgb: 255, 255, 255;
--ion-color-success-shade: #28ba62;
--ion-color-success-tint: #42d77d;
/** warning **/
--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;
/** danger **/
--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;
/** dark **/
--ion-color-dark: #222428;
--ion-color-dark-rgb: 34, 36, 40;
--ion-color-dark-contrast: #ffffff;
--ion-color-dark-contrast-rgb: 255, 255, 255;
--ion-color-dark-shade: #1e2023;
--ion-color-dark-tint: #383a3e;
/** medium **/
--ion-color-medium: #92949c;
--ion-color-medium-rgb: 146, 148, 156;
--ion-color-medium-contrast: #ffffff;
--ion-color-medium-contrast-rgb: 255, 255, 255;
--ion-color-medium-shade: #808289;
--ion-color-medium-tint: #9d9fa6;
/** light **/
--ion-color-light: #f4f5f8;
--ion-color-light-rgb: 244, 245, 248;
--ion-color-light-contrast: #000000;
--ion-color-light-contrast-rgb: 0, 0, 0;
--ion-color-light-shade: #d7d8da;
--ion-color-light-tint: #f5f6f9;
}
}

View File

@@ -0,0 +1,96 @@
import {
IonButton,
IonButtons,
IonCol,
IonContent,
IonHeader,
IonIcon,
IonPage,
IonRow,
IonTitle,
IonToolbar,
useIonRouter,
} from '@ionic/react';
import { Geolocation } from '@capacitor/geolocation';
import { useEffect, useState } from 'react';
import { SkeletonDashboard } from '../components/SkeletonDashboard';
import { chevronBackOutline, refreshOutline } from 'ionicons/icons';
import { CurrentWeather } from '../components/CurrentWeather';
function Tab1() {
const router = useIonRouter();
const [currentWeather, setCurrentWeather] = useState(false);
useEffect(() => {
getCurrentPosition();
}, []);
const getCurrentPosition = async () => {
setCurrentWeather(false);
const coordinates = await Geolocation.getCurrentPosition();
getAddress(coordinates.coords);
};
const getAddress = async (coords) => {
const query = `${coords.latitude},${coords.longitude}`;
const response = await fetch(
`https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}`
);
const data = await response.json();
console.log(data);
setCurrentWeather(data);
};
// const router = useIonRouter();
function handleBackClick() {
router.goBack();
}
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>My Weather</IonTitle>
<IonButtons slot="end">
<IonButton onClick={() => getCurrentPosition()}>
<IonIcon icon={refreshOutline} color="primary" />
</IonButton>
</IonButtons>
<IonButtons slot="start">
<IonButton onClick={() => handleBackClick()}>
<IonIcon icon={chevronBackOutline} color="primary" />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Dashboard</IonTitle>
</IonToolbar>
</IonHeader>
<IonRow className="ion-margin-start ion-margin-end ion-justify-content-center ion-text-center">
<IonCol size="12">
<h4>Here's your location based weather</h4>
</IonCol>
</IonRow>
<div style={{ marginTop: '-1.5rem' }}>
{currentWeather ? (
<CurrentWeather currentWeather={currentWeather} />
) : (
<SkeletonDashboard />
)}
</div>
</IonContent>
</IonPage>
);
}
export default Tab1;

View File

@@ -0,0 +1,81 @@
import {
IonButton,
IonCol,
IonContent,
IonHeader,
IonPage,
IonRow,
IonSearchbar,
IonTitle,
IonToolbar,
} from '@ionic/react';
import { useState } from 'react';
import { CurrentWeather } from '../components/CurrentWeather';
function Tab2() {
const [search, setSearch] = useState('');
const [currentWeather, setCurrentWeather] = useState(false);
const performSearch = async () => {
getAddress(search);
};
const getAddress = async (city) => {
const response = await fetch(
`https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no`
);
const data = await response.json();
if (data && data.current && data.location) {
setCurrentWeather(data);
}
};
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Search</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Search</IonTitle>
</IonToolbar>
</IonHeader>
<IonRow className="ion-justify-content-center ion-margin-top ion-align-items-center">
<IonCol size="7">
<IonSearchbar
placeholder="Try 'London'"
animated
value={search}
onIonChange={(e) => setSearch(e.target.value)}
/>
</IonCol>
<IonCol size="5">
<IonButton
expand="block"
className="ion-margin-start ion-margin-end"
onClick={performSearch}
>
Search
</IonButton>
</IonCol>
</IonRow>
<div style={{ marginTop: '-0.8rem' }}>
{currentWeather ? (
<CurrentWeather currentWeather={currentWeather} />
) : (
<h3 className="ion-text-center">Your search result will appear here</h3>
)}
</div>
</IonContent>
</IonPage>
);
}
export default Tab2;

View File

@@ -0,0 +1,62 @@
import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react';
import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons';
import { useEffect, useState } from 'react';
export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => {
const [property, setProperty] = useState(false);
const properties = {
wind: {
isIcon: false,
icon: '/assets/WeatherDemo/wind.png',
alt: 'wind',
label: 'Wind',
value: `${currentWeather.current.wind_mph}mph`,
},
feelsLike: {
isIcon: true,
icon: thermometerOutline,
alt: 'feels like',
label: 'Feels like',
value: `${currentWeather.current.feelslike_c}°C`,
},
indexUV: {
isIcon: true,
icon: sunnyOutline,
alt: 'index uv',
label: 'Index UV',
value: currentWeather.current.uv,
},
pressure: {
isIcon: true,
icon: pulseOutline,
alt: 'pressure',
label: 'Pressure',
value: `${currentWeather.current.pressure_mb} mbar`,
},
};
useEffect(() => {
setProperty(properties[type]);
}, [type]);
return (
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
{!property.isIcon && (
<img alt={property.alt} src={property.icon} height="32" width="32" />
)}
{property.isIcon && (
<IonIcon icon={property.icon} color="medium" style={{ fontSize: '2rem' }} />
)}
</IonCol>
<IonCol size="9">
<IonCardSubtitle>{property.label}</IonCardSubtitle>
<IonNote>{property.value}</IonNote>
</IonCol>
</IonRow>
</IonCol>
);
};

View File

@@ -0,0 +1,48 @@
import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react';
import { WeatherProperty } from './WeatherProperty';
export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => (
<IonGrid>
<IonCard>
<IonCardContent className="ion-text-center">
<IonText color="primary">
<h1>
{currentWeather.location.region},{' '}
<span style={{ color: 'gray' }}>{currentWeather.location.country}</span>
</h1>
</IonText>
<div className="ion-margin-top">
<img
alt="condition"
src={currentWeather.current.condition.icon.replace('//', 'https://')}
/>
<IonText color="dark">
<h1 style={{ fontWeight: 'bold' }}>{currentWeather.current.condition.text}</h1>
</IonText>
<IonText color="medium">
<p>{new Date(currentWeather.location.localtime).toDateString()}</p>
</IonText>
</div>
<IonCardTitle style={{ fontSize: '3rem' }} className="ion-margin-top">
{currentWeather.current.temp_c}&#8451;
</IonCardTitle>
<IonGrid className="ion-margin-top">
<IonRow>
<WeatherProperty type="wind" currentWeather={currentWeather} />
<WeatherProperty type="feelsLike" currentWeather={currentWeather} />
</IonRow>
<IonRow className="ion-margin-top">
<WeatherProperty type="indexUV" currentWeather={currentWeather} />
<WeatherProperty type="pressure" currentWeather={currentWeather} />
</IonRow>
</IonGrid>
</IonCardContent>
</IonCard>
</IonGrid>
);

View File

@@ -0,0 +1,24 @@
.container {
text-align: center;
position: absolute;
left: 0;
right: 0;
top: 50%;
transform: translateY(-50%);
}
.container strong {
font-size: 20px;
line-height: 26px;
}
.container p {
font-size: 16px;
line-height: 22px;
color: #8c8c8c;
margin: 0;
}
.container a {
text-decoration: none;
}

View File

@@ -0,0 +1,14 @@
import './ExploreContainer.css';
interface ContainerProps { }
const ExploreContainer: React.FC<ContainerProps> = () => {
return (
<div className="container">
<strong>Ready to create an app?</strong>
<p>Start with Ionic <a target="_blank" rel="noopener noreferrer" href="https://ionicframework.com/docs/components">UI Components</a></p>
</div>
);
};
export default ExploreContainer;

Some files were not shown because too many files have changed in this diff Show More