feat: implement Hotel Service Wi-Fi intro page with Swiper slider and HTML content display
88
03_source/mobile/package-lock.json
generated
@@ -47,9 +47,10 @@
|
||||
"react-spinners": "^0.17.0",
|
||||
"react-star-ratings": "^2.3.0",
|
||||
"react-use": "^17.6.0",
|
||||
"react-virtuoso": "^4.13.0",
|
||||
"reselect": "^4.0.0",
|
||||
"sass": "^1.59.3",
|
||||
"swiper": "^9.1.1",
|
||||
"swiper": "^11.2.8",
|
||||
"use-sound": "^5.0.0",
|
||||
"zod": "^3.25.56"
|
||||
},
|
||||
@@ -2073,9 +2074,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/decode-named-character-reference": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.1.0.tgz",
|
||||
"integrity": "sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz",
|
||||
"integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"character-entities": "^2.0.0"
|
||||
@@ -4937,6 +4938,16 @@
|
||||
"react-dom": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-virtuoso": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.13.0.tgz",
|
||||
"integrity": "sha512-XHv2Fglpx80yFPdjZkV9d1baACKghg/ucpDFEXwaix7z0AfVQj+mF6lM+YQR6UC/TwzXG2rJKydRMb3+7iV3PA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">=16 || >=17 || >= 18 || >= 19",
|
||||
"react-dom": ">=16 || >=17 || >= 18 || >=19"
|
||||
}
|
||||
},
|
||||
"node_modules/reactcss": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
|
||||
@@ -5300,11 +5311,6 @@
|
||||
"node": ">= 10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/ssr-window": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-4.0.2.tgz",
|
||||
"integrity": "sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ=="
|
||||
},
|
||||
"node_modules/stack-generator": {
|
||||
"version": "2.0.10",
|
||||
"resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz",
|
||||
@@ -5427,18 +5433,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/style-to-js": {
|
||||
"version": "1.1.16",
|
||||
"resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.16.tgz",
|
||||
"integrity": "sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw==",
|
||||
"version": "1.1.17",
|
||||
"resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.17.tgz",
|
||||
"integrity": "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"style-to-object": "1.0.8"
|
||||
"style-to-object": "1.0.9"
|
||||
}
|
||||
},
|
||||
"node_modules/style-to-object": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz",
|
||||
"integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==",
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.9.tgz",
|
||||
"integrity": "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inline-style-parser": "0.2.4"
|
||||
@@ -5484,9 +5490,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/swiper": {
|
||||
"version": "9.1.1",
|
||||
"resolved": "https://registry.npmjs.org/swiper/-/swiper-9.1.1.tgz",
|
||||
"integrity": "sha512-D1zArOwI6XCXCYBULPA4jTxpqp5SQtvntjinbXNZwXzj6P3KS51zSWuMarCLXq5oRISay4nX+TuShpxz8qhtbw==",
|
||||
"version": "11.2.8",
|
||||
"resolved": "https://registry.npmjs.org/swiper/-/swiper-11.2.8.tgz",
|
||||
"integrity": "sha512-S5FVf6zWynPWooi7pJ7lZhSUe2snTzqLuUzbd5h5PHUOhzgvW0bLKBd2wv0ixn6/5o9vwc/IkQT74CRcLJQzeg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "patreon",
|
||||
@@ -5497,9 +5503,7 @@
|
||||
"url": "http://opencollective.com/swiper"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"ssr-window": "^4.0.2"
|
||||
},
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 4.7.0"
|
||||
}
|
||||
@@ -7588,9 +7592,9 @@
|
||||
}
|
||||
},
|
||||
"decode-named-character-reference": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.1.0.tgz",
|
||||
"integrity": "sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz",
|
||||
"integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==",
|
||||
"requires": {
|
||||
"character-entities": "^2.0.0"
|
||||
}
|
||||
@@ -9482,6 +9486,12 @@
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"react-virtuoso": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.13.0.tgz",
|
||||
"integrity": "sha512-XHv2Fglpx80yFPdjZkV9d1baACKghg/ucpDFEXwaix7z0AfVQj+mF6lM+YQR6UC/TwzXG2rJKydRMb3+7iV3PA==",
|
||||
"requires": {}
|
||||
},
|
||||
"reactcss": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
|
||||
@@ -9738,11 +9748,6 @@
|
||||
"integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==",
|
||||
"dev": true
|
||||
},
|
||||
"ssr-window": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-4.0.2.tgz",
|
||||
"integrity": "sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ=="
|
||||
},
|
||||
"stack-generator": {
|
||||
"version": "2.0.10",
|
||||
"resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz",
|
||||
@@ -9841,17 +9846,17 @@
|
||||
"dev": true
|
||||
},
|
||||
"style-to-js": {
|
||||
"version": "1.1.16",
|
||||
"resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.16.tgz",
|
||||
"integrity": "sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw==",
|
||||
"version": "1.1.17",
|
||||
"resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.17.tgz",
|
||||
"integrity": "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==",
|
||||
"requires": {
|
||||
"style-to-object": "1.0.8"
|
||||
"style-to-object": "1.0.9"
|
||||
}
|
||||
},
|
||||
"style-to-object": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz",
|
||||
"integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==",
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.9.tgz",
|
||||
"integrity": "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==",
|
||||
"requires": {
|
||||
"inline-style-parser": "0.2.4"
|
||||
}
|
||||
@@ -9885,12 +9890,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"swiper": {
|
||||
"version": "9.1.1",
|
||||
"resolved": "https://registry.npmjs.org/swiper/-/swiper-9.1.1.tgz",
|
||||
"integrity": "sha512-D1zArOwI6XCXCYBULPA4jTxpqp5SQtvntjinbXNZwXzj6P3KS51zSWuMarCLXq5oRISay4nX+TuShpxz8qhtbw==",
|
||||
"requires": {
|
||||
"ssr-window": "^4.0.2"
|
||||
}
|
||||
"version": "11.2.8",
|
||||
"resolved": "https://registry.npmjs.org/swiper/-/swiper-11.2.8.tgz",
|
||||
"integrity": "sha512-S5FVf6zWynPWooi7pJ7lZhSUe2snTzqLuUzbd5h5PHUOhzgvW0bLKBd2wv0ixn6/5o9vwc/IkQT74CRcLJQzeg=="
|
||||
},
|
||||
"tar": {
|
||||
"version": "6.1.13",
|
||||
|
@@ -45,9 +45,10 @@
|
||||
"react-spinners": "^0.17.0",
|
||||
"react-star-ratings": "^2.3.0",
|
||||
"react-use": "^17.6.0",
|
||||
"react-virtuoso": "^4.13.0",
|
||||
"reselect": "^4.0.0",
|
||||
"sass": "^1.59.3",
|
||||
"swiper": "^9.1.1",
|
||||
"swiper": "^11.2.8",
|
||||
"use-sound": "^5.0.0",
|
||||
"zod": "^3.25.56"
|
||||
},
|
||||
|
@@ -71,6 +71,8 @@ import DummyEventPayPage from './pages/DummyEventPayPage';
|
||||
import PaymentSuccess from './pages/PaymentSuccess';
|
||||
import PaymentFailed from './pages/PaymentFailed';
|
||||
import settings from './pages/tabs/carousell_me/settings';
|
||||
import HotelWelcomeTour from './pages/HotelWelcomeTour';
|
||||
import HotelServiceWifi from './pages/HotelIntro';
|
||||
|
||||
setupIonicReact();
|
||||
|
||||
@@ -150,6 +152,9 @@ const IonicApp: React.FC<IonicAppProps> = ({ darkMode, schedule, setIsLoggedIn,
|
||||
<Route exact={true} path="/order_detail/:id" component={OrderDetail} />
|
||||
|
||||
<Route exact={true} path="/helloworld" component={Helloworld} />
|
||||
<Route path={PATHS.HOTEL_WELCOME_TOUR} component={HotelWelcomeTour} exact={true} />
|
||||
{/* <Route path={PATHS.HOTEL_SERVICE_INTRO} component={HotelServiceIntro} exact={true} /> */}
|
||||
{/* tabs/hotel_service_intro */}
|
||||
|
||||
<Route
|
||||
path="/logout"
|
||||
|
@@ -107,5 +107,10 @@ const PATHS = {
|
||||
CAROUSELL_ME: '/tabs/carousell_me',
|
||||
CAROUSELL_ME_QR: '/tabs/carousell_me/qr_page',
|
||||
CAROUSELL_ME_SETTINGS: '/carousell_me/settings',
|
||||
CAROUSELL_ME_INSIGHTS: '/tabs/carousell_me/insights',
|
||||
//
|
||||
HOTEL_WELCOME_TOUR: '/hotel_service_intro',
|
||||
HOTEL_INTRO: '/tabs/hotel_intro',
|
||||
HOTEL_SERVICE_WIFI: '/tabs/hotel_service_wifi',
|
||||
};
|
||||
export default PATHS;
|
||||
|
95
03_source/mobile/src/api/getContentMarkdown.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
import React from 'react';
|
||||
|
||||
const contentMd = `
|
||||
全館Wi-Fiをご利用いただけます。(無料)
|
||||
|
||||
|
||||
|
||||
■ID: helloworld Life
|
||||
■Password: 0362751510
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
LYNKED HOTELでは、
|
||||
|
||||
全客室・宴会場の一部に
|
||||
|
||||
有線LAN接続によるインターネット
|
||||
|
||||
接続環境を開業時より
|
||||
|
||||
導入しておりましたが、
|
||||
|
||||
さらなる利便性向上を図るため、
|
||||
|
||||
このたび、ロビー・客室フロアに無線LAN
|
||||
|
||||
アクセスポイントを設置し、
|
||||
|
||||
各客室内でもWi-Fi接続による
|
||||
|
||||
インターネットを利用できる
|
||||
|
||||
環境を構築いたしました。
|
||||
|
||||
より快適なWi-Fi接続サービスによる
|
||||
|
||||
無料のインターネット接続を
|
||||
|
||||
ご利用いただけます。
|
||||
|
||||
|
||||
|
||||
■ ご利用いただける端末
|
||||
|
||||
・LAN / 無線LAN(Wi-Fi規格)アダプタ内蔵PC
|
||||
|
||||
・WindowsXP以上、Mac OSX以上のOSを搭載したPC
|
||||
|
||||
・無線LAN(Wi-Fi規格)対応のiOS機器(iPhone・iPad等)
|
||||
|
||||
・無線LAN(Wi-Fi規格)のAndroid機器(スマートフォン・タブレットPC等)
|
||||
|
||||
|
||||
■ Wi-Fi接続サービスの規格・接続
|
||||
|
||||
IEEE802.11 n/b/gの規格に準拠しており、同時使用が可能です。
|
||||
|
||||
ネットアクセスポイントにつきましては、各ホテルSSIDをフロントにてご案内いたします。
|
||||
|
||||
|
||||
■ 各ホテル内でインターネット接続サービスが可能なエリア
|
||||
|
||||
・全客室内(有線LAN・Wi-Fi接続)
|
||||
|
||||
・フロント前ロビー(Wi-Fi接続)
|
||||
|
||||
・各宴会場(有線LAN接続)
|
||||
|
||||
|
||||
■ ご注意
|
||||
|
||||
※有線・無線LANを通してインターネット接続サービスを無料でご利用いただけます。
|
||||
|
||||
|
||||
※Wi-Fi接続サービスは、ホテルのSSIDが発出されている場所でご利用になれますが、場所により電波の届かないエリア、もしくは電波が弱くご利用が難しい場合もあります。
|
||||
|
||||
|
||||
※ご利用に際してのセキュリティ設定は、お客様ご自身の責任において行っていただくようにお願いいたします。
|
||||
|
||||
|
||||
※本サービスのご利用・予期せぬ停止や不良が原因となり発生した損失や損害については、ホテルは一切の責任は負いかねますので、予めご了承ください。
|
||||
|
||||
|
||||
※ネット対戦ゲーム、大容量ファイルの送受信など、回線を長時間占有してのご利用は、他のお客様のご迷惑となりますので、ご遠慮ください。
|
||||
`.trim();
|
||||
|
||||
function getContentMarkdown(): Promise<string> {
|
||||
return new Promise((res, rej) => {
|
||||
res(contentMd);
|
||||
});
|
||||
}
|
||||
|
||||
export default getContentMarkdown;
|
119
03_source/mobile/src/api/getHtmlContent.tsx
Normal file
@@ -0,0 +1,119 @@
|
||||
import React from 'react';
|
||||
|
||||
const testHtml = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
<style>
|
||||
.asscent-color1 { color: #800000 }
|
||||
.text-deep-grey {color: rgb(0,0,0)}
|
||||
.text-grey {color: rgb(0,0,0, 0.6)}
|
||||
.text-smoothing {opacity: 0.8}
|
||||
h1 { font-size: 1.2rem}
|
||||
h2 { font-size: 1.1rem}
|
||||
h3 { font-size: 1rem}
|
||||
h4 { font-size: 0.8rem}
|
||||
h5 { font-size: 0.7rem}
|
||||
h6 { font-size: 0.6rem}
|
||||
|
||||
.quote {
|
||||
background-color: rgba(231, 76, 60, 0.05);
|
||||
padding: 1rem; border-radius: 5px;
|
||||
border-left: 5px solid #800000;
|
||||
width: 90%;
|
||||
margin: 1rem auto;
|
||||
line-height:2rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body style="padding: 0; margin: 0; box-sizing: border-box;" class="text-smoothing">
|
||||
<div class="text-grey" style="font-size: 0.8rem; text-align: center">
|
||||
全館Wi-Fiをご利用いただけます。(無料)
|
||||
</div>
|
||||
|
||||
<div class="quote">
|
||||
<div>■ ID: hello_user</div>
|
||||
<div>■ Password: my-best-password</div>
|
||||
</div>
|
||||
|
||||
<h3 class="asscent-color1">HELLOWORLD HOTELでは、</h3>
|
||||
<p>全客室・宴会場の一部に</p>
|
||||
<p>有線LAN接続によるインターネット</p>
|
||||
<p>接続環境を開業時より</p>
|
||||
<p>導入しておりましたが、</p>
|
||||
<p>さらなる利便性向上を図るため、</p>
|
||||
<p>このたび、ロビー・客室フロアに無線LAN</p>
|
||||
<p>アクセスポイントを設置し、</p>
|
||||
<p>各客室内でもWi-Fi接続による</p>
|
||||
<p>インターネットを利用できる</p>
|
||||
<p>環境を構築いたしました。</p>
|
||||
<p>より快適なWi-Fi接続サービスによる</p>
|
||||
<p>無料のインターネット接続を</p>
|
||||
<p>ご利用いただけます。</p>
|
||||
|
||||
|
||||
<h4 class="asscent-color1">■ ご利用いただける端末</h4>
|
||||
<p>・LAN / 無線LAN(Wi-Fi規格)アダプタ内蔵PC</p>
|
||||
<p>・WindowsXP以上、Mac OSX以上のOSを搭載したPC</p>
|
||||
<p>・無線LAN(Wi-Fi規格)対応のiOS機器(iPhone・iPad等)</p>
|
||||
<p>・無線LAN(Wi-Fi規格)のAndroid機器(スマートフォン・タブレットPC等)</p>
|
||||
|
||||
<h4 class="asscent-color1">■ Wi-Fi接続サービスの規格・接続</h4>
|
||||
<p>IEEE802.11 n/b/gの規格に準拠しており、同時使用が可能です。</p>
|
||||
<p>ネットアクセスポイントにつきましては、各ホテルSSIDをフロントにてご案内いたします。</p>
|
||||
|
||||
<h4 class="asscent-color1">■ 各ホテル内でインターネット接続サービスが可能なエリア</h4>
|
||||
<p>・全客室内(有線LAN・Wi-Fi接続)</p>
|
||||
<p>・フロント前ロビー(Wi-Fi接続)</p>
|
||||
<p>・各宴会場(有線LAN接続)</p>
|
||||
|
||||
<h4 class="asscent-color1">■ ご注意</h4>
|
||||
<p>※有線・無線LANを通してインターネット接続サービスを無料でご利用いただけます。</p>
|
||||
<p>※Wi-Fi接続サービスは、ホテルのSSIDが発出されている場所でご利用になれますが、場所により電波の届かないエリア、もしくは電波が弱くご利用が難しい場合もあります。</p>
|
||||
<p>※ご利用に際してのセキュリティ設定は、お客様ご自身の責任において行っていただくようにお願いいたします。</p>
|
||||
<p>※本サービスのご利用・予期せぬ停止や不良が原因となり発生した損失や損害については、ホテルは一切の責任は負いかねますので、予めご了承ください。</p>
|
||||
<p>※ネット対戦ゲーム、大容量ファイルの送受信など、回線を長時間占有してのご利用は、他のお客様のご迷惑となりますので、ご遠慮ください。</p>
|
||||
|
||||
<h1>待進變果沒致友環健問水法代人苦天。📅📍🎻</h1>
|
||||
<h2>待進變果沒致友環健問水法代人苦天。📅📍🎻</h2>
|
||||
<h3>待進變果沒致友環健問水法代人苦天。📅📍🎻</h3>
|
||||
<h4>待進變果沒致友環健問水法代人苦天。📅📍🎻</h4>
|
||||
<h5>待進變果沒致友環健問水法代人苦天。📅📍🎻</h5>
|
||||
<h6>待進變果沒致友環健問水法代人苦天。📅📍🎻</h6>
|
||||
|
||||
<p>
|
||||
業立臺四即文善公作有往,等怕準命小電個。
|
||||
査今聞光洋後化外財強主職。
|
||||
🌲🔯🍣💵 🐪👫🐈📅📍🎻💼 🐣🍖🐻📩🍨. 🎇👬💨
|
||||
</p>
|
||||
|
||||
|
||||
<h1>Lorem Ipsum: Usage, Common examples, Translation, Variants and technical information📅📍🎻</h1>
|
||||
<h2>Lorem Ipsum: Usage, Common examples, Translation, Variants and technical information📅📍🎻</h2>
|
||||
<h3>Lorem Ipsum: Usage, Common examples, Translation, Variants and technical information📅📍🎻</h3>
|
||||
<h4>Lorem Ipsum: Usage, Common examples, Translation, Variants and technical information📅📍🎻</h4>
|
||||
<h5>Lorem Ipsum: Usage, Common examples, Translation, Variants and technical information📅📍🎻</h5>
|
||||
<h6>Lorem Ipsum: Usage, Common examples, Translation, Variants and technical information📅📍🎻</h6>
|
||||
|
||||
<p>
|
||||
Lorem Ipsum: Usage, Common examples, Translation, Variants and technical information
|
||||
Essay: Lorem Ipsum--when, and when not to use it
|
||||
🌲🔯🍣💵 🐪👫🐈📅📍🎻💼 🐣🍖🐻📩🍨. 🎇👬💨
|
||||
</p>
|
||||
|
||||
<p>📤🏮👀🍮 💃👪👦🌀🌶📈 🍵📊💓🐧🎢👃 🍕🌛🔎🔋🎣🍃 🎡👩📔🍈💭 🎣👅🔽📟📑💋</p>
|
||||
|
||||
</body>
|
||||
</html>`.trim();
|
||||
|
||||
function getHtmlContent(): Promise<string> {
|
||||
return new Promise((res, rej) => {
|
||||
res(testHtml);
|
||||
});
|
||||
}
|
||||
|
||||
export default getHtmlContent;
|
11
03_source/mobile/src/api/getUnsplashRandomImage.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
|
||||
function getRandomInt(max: number) {
|
||||
return Math.floor(Math.random() * max);
|
||||
}
|
||||
|
||||
function getUnsplashRandomImage({ keyword }: { keyword: string }) {
|
||||
return `https://media.karousell.com/media/photos/profiles/2025/02/05/louis_coding_1738774979_f1598e0b.jpg`;
|
||||
}
|
||||
|
||||
export default getUnsplashRandomImage;
|
221
03_source/mobile/src/pages/HotelIntro/index.tsx
Normal file
@@ -0,0 +1,221 @@
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import {
|
||||
IonContent,
|
||||
IonPage,
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonButtons,
|
||||
IonButton,
|
||||
IonIcon,
|
||||
useIonViewWillEnter,
|
||||
IonBackButton,
|
||||
IonText,
|
||||
} from '@ionic/react';
|
||||
import { arrowForward, share, star, starOutline } from 'ionicons/icons';
|
||||
import { setMenuEnabled } from '../../data/sessions/sessions.actions';
|
||||
import { setHasSeenTutorial } from '../../data/user/user.actions';
|
||||
import './style.scss';
|
||||
import { connect } from '../../data/connect';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import PATHS from '../../PATHS';
|
||||
import getUnsplashRandomImage from '../../api/getUnsplashRandomImage';
|
||||
import schedulePng from './sampleHeader.png';
|
||||
|
||||
// import type { Swiper } from 'swiper/types';
|
||||
// import type { Swiper as SwiperClass } from 'swiper/types';
|
||||
import { Swiper, SwiperSlide } from 'swiper/react';
|
||||
import 'swiper/css';
|
||||
import '@ionic/react/css/ionic-swiper.css';
|
||||
|
||||
import getHtmlContent from '../../api/getHtmlContent';
|
||||
|
||||
interface OwnProps extends RouteComponentProps {}
|
||||
interface DispatchProps {
|
||||
setHasSeenTutorial: typeof setHasSeenTutorial;
|
||||
setMenuEnabled: typeof setMenuEnabled;
|
||||
}
|
||||
|
||||
interface TutorialProps extends OwnProps, DispatchProps {}
|
||||
|
||||
const HotelServiceWifi: React.FC<TutorialProps> = () => {
|
||||
const [htmlContent, setHtmlContent] = useState<string>('');
|
||||
let [loadingContent, setLoadingContent] = useState<boolean>(true);
|
||||
useEffect(() => {
|
||||
getHtmlContent()
|
||||
.then((res) => setHtmlContent(res.trim()))
|
||||
.then(() => setLoadingContent(false));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<IonPage id="hotel-service-intro-page">
|
||||
<IonHeader className="ion-no-border">
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonBackButton defaultHref="/tabs/schedule"></IonBackButton>
|
||||
</IonButtons>
|
||||
<IonButtons slot="end">
|
||||
<IonButton onClick={() => {}}>
|
||||
{false ? (
|
||||
<IonIcon slot="icon-only" icon={star}></IonIcon>
|
||||
) : (
|
||||
<IonIcon slot="icon-only" icon={starOutline}></IonIcon>
|
||||
)}
|
||||
</IonButton>
|
||||
<IonButton onClick={() => {}}>
|
||||
<IonIcon slot="icon-only" icon={share}></IonIcon>
|
||||
</IonButton>
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent>
|
||||
{/* helloworld */}
|
||||
<Swiper>
|
||||
<SwiperSlide>
|
||||
<div
|
||||
style={{
|
||||
width: '100vw',
|
||||
height: '33vh',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
backgroundImage: `url(//plus.unsplash.com/premium_photo-1661964071015-d97428970584)`,
|
||||
}}
|
||||
></div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div
|
||||
style={{
|
||||
width: '100vw',
|
||||
height: '33vh',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
backgroundImage: `url(//images.unsplash.com/photo-1618773928121-c32242e63f39)`,
|
||||
}}
|
||||
></div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div
|
||||
style={{
|
||||
width: '100vw',
|
||||
height: '33vh',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
backgroundImage: `url(//images.unsplash.com/photo-1551882547-ff40c63fe5fa)`,
|
||||
}}
|
||||
></div>
|
||||
</SwiperSlide>
|
||||
</Swiper>
|
||||
|
||||
{/* */}
|
||||
<div
|
||||
id="content"
|
||||
style={{
|
||||
paddingLeft: '0.5rem',
|
||||
paddingRight: '0.5rem',
|
||||
marginTop: '1rem',
|
||||
marginBottom: '1rem',
|
||||
}}
|
||||
>
|
||||
<div dangerouslySetInnerHTML={{ __html: contentHtml }}>{}</div>
|
||||
</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect<OwnProps, {}, DispatchProps>({
|
||||
mapDispatchToProps: {
|
||||
setHasSeenTutorial,
|
||||
setMenuEnabled,
|
||||
},
|
||||
component: HotelServiceWifi,
|
||||
});
|
||||
|
||||
const contentHtml = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
|
||||
<style>
|
||||
body {padding:0; margin:0; box-sizing: border-box;}
|
||||
.text { color: rgba(0,0,0,0.8)}
|
||||
.text-grey {color: rgba(0,0,0,0.5)}
|
||||
.text-asscent {color: rgba(64,0,0,0.8)}
|
||||
.bold {font-weight: bold;}
|
||||
.description {font-size: 1rem}
|
||||
.title { font-size: 1.1rem }
|
||||
.text-subtitle { font-size: 0.8rem }
|
||||
.center { text-align: center}
|
||||
.hr-asscent { border-bottom: 3px solid rgba(64,0,0,0.8) }
|
||||
.hr-grey { border-bottom: 1px solid rgba(64,64,64,0.5) }
|
||||
.asscent-background { background-color: rgba(64,0,0,0.1); border-radius: 10px; padding: 1rem 0.5rem }
|
||||
td {padding-right: 2rem; font-size: 0.8rem}
|
||||
.horizontal-spacer { padding: 2rem 0}
|
||||
.shadow { box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px; }
|
||||
.hard-shadow { box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="text bold center horizontal-spacer">
|
||||
<h3>helloworld Hotel</h3>
|
||||
</div>
|
||||
|
||||
<div class="text-grey description">〒107-0062 東京都港区南青山1丁目26-1</div>
|
||||
<hr class="hr-asscent">
|
||||
|
||||
<div class="asscent-background shadow">
|
||||
<div class="text-asscent">ホテル情報</div>
|
||||
<hr class="hr-grey" >
|
||||
<table>
|
||||
<tr>
|
||||
<td class="text-grey">チェックイン</td><td class="text-grey">15:00</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-grey">チェックアウト</td><td class="text-grey">10:00</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-grey">夜間玄関施錠</td><td class="text-grey">25:00</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="text-asscent center">
|
||||
<h5>ご利用可能なクレジットカード</h5>
|
||||
</div>
|
||||
<hr class="hr-grey" >
|
||||
|
||||
<div class="horizontal-spacer">
|
||||
<div class="text-asscent bold center">helloworld 物語</div>
|
||||
<div class="text-subtitle bold center">"TRAVEL IS LIFE, DX IS HOW"</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="horizontal-spacer shadow"
|
||||
style="
|
||||
width:100%;
|
||||
height: 200px;
|
||||
|
||||
background-image: url('https://images.unsplash.com/photo-1566073771259-6a8506099945');
|
||||
background-size:cover;
|
||||
background-position:center;
|
||||
background-repeat: no-repeat;
|
||||
border-radius: 10px;
|
||||
"></div>
|
||||
|
||||
<div class="horizontal-spacer text-grey bold description">
|
||||
<p>異国情緒あふれる館に招かれたような優雅なひととき。</p>
|
||||
|
||||
<p>helloworld LifeHOTELはヨーロッパを中心とした様々なコンセプトで全国に展開しており、ホテルに一歩足を踏み入れた瞬間から、異国情緒あふれるLYNKEDならではの世界観を楽しんでいただけます。</p>
|
||||
<p>ご宿泊の際は、1日の締めくくりにほっとくつろいでいただけるあたたかさと機能性を兼ね備えた客室で。</p>
|
||||
<p>邸宅に招かれたような雰囲気を兼ね備えたレストランでのお食事は、記念日など特別な日にも。</p>
|
||||
<p>オーセンティックな教会や華やかで明るいパーティールームで最高にロマンティックな結婚式を。</p>
|
||||
<p>そのほか各種パーティーや会議、天然温泉をひいたスパ施設など、心あたたまるおもてなしで、みなさまをお迎えいたします。</p>
|
||||
<p>helloworld HOTELのドラマティックステージへようこそ。</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
BIN
03_source/mobile/src/pages/HotelIntro/product.png
Normal file
After Width: | Height: | Size: 180 KiB |
BIN
03_source/mobile/src/pages/HotelIntro/profile.png
Normal file
After Width: | Height: | Size: 123 KiB |
BIN
03_source/mobile/src/pages/HotelIntro/sampleHeader.png
Normal file
After Width: | Height: | Size: 976 KiB |
BIN
03_source/mobile/src/pages/HotelIntro/schedule.png
Normal file
After Width: | Height: | Size: 80 KiB |
@@ -10,7 +10,7 @@
|
||||
|
||||
.slider {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 100%);
|
||||
// grid-template-columns: repeat(4, 100%);
|
||||
grid-template-rows: 1fr;
|
||||
|
||||
height: 100%;
|
210
03_source/mobile/src/pages/HotelServiceIntro/index.tsx
Normal file
@@ -0,0 +1,210 @@
|
||||
import {
|
||||
IonButton,
|
||||
IonButtons,
|
||||
IonContent,
|
||||
IonHeader,
|
||||
IonIcon,
|
||||
IonPage,
|
||||
IonText,
|
||||
IonToolbar,
|
||||
useIonRouter,
|
||||
} from '@ionic/react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { star, starOutline, share, chevronBack } from 'ionicons/icons';
|
||||
|
||||
// import type { Swiper } from 'swiper/types';
|
||||
// import type { Swiper as SwiperClass } from 'swiper/types';
|
||||
import { Swiper, SwiperSlide } from 'swiper/react';
|
||||
import 'swiper/css';
|
||||
import '@ionic/react/css/ionic-swiper.css';
|
||||
|
||||
import './style.scss';
|
||||
|
||||
import Sampleheader from './sampleHeader.png';
|
||||
import getUnsplashRandomImage from '../../api/getUnsplashRandomImage';
|
||||
// import ContentHtml from './ContentHtml.tsx.del';
|
||||
import getHtmlContent from '../../api/getHtmlContent';
|
||||
import PATHS from '../../PATHS';
|
||||
|
||||
const tutorialSample1 = {
|
||||
image: getUnsplashRandomImage({ keyword: 'seafood' }),
|
||||
title: `
|
||||
## ホテルの利益率は?`.trim(),
|
||||
content: `
|
||||
新型コロナウイルス感染症拡大により、ホテル業界は厳しい経営を強いられる中、ウィズコロナに向けて。`.trim(),
|
||||
};
|
||||
|
||||
const tutorialSample2 = {
|
||||
image: getUnsplashRandomImage({ keyword: 'hotel' }),
|
||||
title: `
|
||||
## ホテルの収益構造`.trim(),
|
||||
content: `
|
||||
ホテルの収益構造を分解すると、以下の3部門に分けられます。`.trim(),
|
||||
};
|
||||
const tutorialSample3 = {
|
||||
image: getUnsplashRandomImage({ keyword: 'hotel' }),
|
||||
title: `
|
||||
## 宿泊部門`.trim(),
|
||||
content: `
|
||||
ホテル・旅館の主軸となるサービスが宿泊部門で、利用者の宿泊に関する料金が計上されます。`.trim(),
|
||||
};
|
||||
const tutorialSample4 = {
|
||||
image: getUnsplashRandomImage({ keyword: 'hotel' }),
|
||||
title: `
|
||||
## 飲食部門`.trim(),
|
||||
content: `
|
||||
ホテルに内設されているレストランやバーで、飲食サービスを提供するのが飲食部門です。`.trim(),
|
||||
};
|
||||
const tutorialSample5 = {
|
||||
image: getUnsplashRandomImage({ keyword: 'hotel' }),
|
||||
title: `
|
||||
## 宴会・イベント部門`.trim(),
|
||||
content: `
|
||||
団体・グループで宴会やイベントを催したい場合に、会場の貸し出しや料理の提供を行うのが宴会・イベント部門です。`.trim(),
|
||||
};
|
||||
|
||||
const tutorialSetup = [
|
||||
tutorialSample1,
|
||||
tutorialSample2,
|
||||
tutorialSample3,
|
||||
tutorialSample4,
|
||||
tutorialSample5,
|
||||
];
|
||||
const tutorialLastSlideIdx = tutorialSetup.length - 1;
|
||||
|
||||
interface HotelServiceIntro {}
|
||||
|
||||
const HotelServiceIntro: React.FC<HotelServiceIntro> = () => {
|
||||
const route = useIonRouter();
|
||||
|
||||
// const pageRef = useRef();
|
||||
// let [swiper, setSwiper] = useState<SwiperCore>();
|
||||
|
||||
const [htmlContent, setHtmlContent] = useState<string>('');
|
||||
let [loadingContent, setLoadingContent] = useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
getHtmlContent()
|
||||
.then((res) => setHtmlContent(res.trim()))
|
||||
.then(() => setLoadingContent(false));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<IonPage id="hotel-service-intro-page">
|
||||
<IonHeader className="no-ion-border">
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonButton onClick={() => route.push(PATHS.EVENT_LIST)}>
|
||||
<IonIcon slot="icon-only" icon={chevronBack}></IonIcon>
|
||||
</IonButton>
|
||||
</IonButtons>
|
||||
<IonButtons slot="end">
|
||||
<IonButton onClick={() => {}}>
|
||||
{false ? (
|
||||
<IonIcon slot="icon-only" icon={star}></IonIcon>
|
||||
) : (
|
||||
<IonIcon slot="icon-only" icon={starOutline}></IonIcon>
|
||||
)}
|
||||
</IonButton>
|
||||
<IonButton onClick={() => {}}>
|
||||
<IonIcon slot="icon-only" icon={share}></IonIcon>
|
||||
</IonButton>
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent>
|
||||
<Swiper>
|
||||
<SwiperSlide>
|
||||
<div
|
||||
style={{
|
||||
width: '100vw',
|
||||
height: '25vh',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
backgroundImage: `url(//plus.unsplash.com/premium_photo-1661964071015-d97428970584)`,
|
||||
}}
|
||||
></div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div
|
||||
style={{
|
||||
width: '100vw',
|
||||
height: '25vh',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
backgroundImage: `url(//images.unsplash.com/photo-1618773928121-c32242e63f39)`,
|
||||
}}
|
||||
></div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div
|
||||
style={{
|
||||
width: '100vw',
|
||||
height: '25vh',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
backgroundImage: `url(//images.unsplash.com/photo-1551882547-ff40c63fe5fa)`,
|
||||
}}
|
||||
></div>
|
||||
</SwiperSlide>
|
||||
</Swiper>
|
||||
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
top: '-50px',
|
||||
left: '1rem',
|
||||
zIndex: 102,
|
||||
height: '50px',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: '100px',
|
||||
height: '100px',
|
||||
backgroundImage: `url("${getUnsplashRandomImage({ keyword: 'hotel' })}")`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
borderRadius: '50px',
|
||||
border: '3px solid white',
|
||||
//
|
||||
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div style={{ paddingLeft: '1rem', paddingRight: '1rem', marginTop: '1rem' }}>
|
||||
{loadingContent ? (
|
||||
<>loading content</>
|
||||
) : (
|
||||
<>
|
||||
<div style={{}}>
|
||||
<div
|
||||
dangerouslySetInnerHTML={{ __html: htmlContent }}
|
||||
style={{ width: '100%', minHeight: '300px' }}
|
||||
></div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
marginTop: '1.5rem',
|
||||
marginBottom: '1.5rem',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<IonText style={{ opacity: '0.5' }}>end</IonText>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(HotelServiceIntro);
|
BIN
03_source/mobile/src/pages/HotelServiceIntro/product.png
Normal file
After Width: | Height: | Size: 180 KiB |
BIN
03_source/mobile/src/pages/HotelServiceIntro/profile.png
Normal file
After Width: | Height: | Size: 123 KiB |
BIN
03_source/mobile/src/pages/HotelServiceIntro/sampleHeader.png
Normal file
After Width: | Height: | Size: 976 KiB |
BIN
03_source/mobile/src/pages/HotelServiceIntro/schedule.png
Normal file
After Width: | Height: | Size: 80 KiB |
2
03_source/mobile/src/pages/HotelServiceIntro/style.scss
Normal file
@@ -0,0 +1,2 @@
|
||||
#hotel-service-intro-page {
|
||||
}
|
126
03_source/mobile/src/pages/HotelServiceWifi/index.tsx
Normal file
@@ -0,0 +1,126 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
IonContent,
|
||||
IonPage,
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonButtons,
|
||||
IonButton,
|
||||
IonIcon,
|
||||
IonBackButton,
|
||||
} from '@ionic/react';
|
||||
import { share, star, starOutline } from 'ionicons/icons';
|
||||
import { setMenuEnabled } from '../../data/sessions/sessions.actions';
|
||||
import { setHasSeenTutorial } from '../../data/user/user.actions';
|
||||
import './style.scss';
|
||||
import { connect } from '../../data/connect';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
|
||||
import { Swiper, SwiperSlide } from 'swiper/react';
|
||||
import 'swiper/css';
|
||||
import '@ionic/react/css/ionic-swiper.css';
|
||||
|
||||
import getHtmlContent from '../../api/getHtmlContent';
|
||||
|
||||
interface OwnProps extends RouteComponentProps {}
|
||||
interface DispatchProps {
|
||||
setHasSeenTutorial: typeof setHasSeenTutorial;
|
||||
setMenuEnabled: typeof setMenuEnabled;
|
||||
}
|
||||
|
||||
interface TutorialProps extends OwnProps, DispatchProps {}
|
||||
|
||||
const HotelServiceWifi: React.FC<TutorialProps> = () => {
|
||||
const [htmlContent, setHtmlContent] = useState<string>('');
|
||||
let [loadingContent, setLoadingContent] = useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
getHtmlContent()
|
||||
.then((res) => setHtmlContent(res.trim()))
|
||||
.then(() => setLoadingContent(false));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<IonPage id="hotel-service-intro-page">
|
||||
<IonHeader className="ion-no-border">
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonBackButton defaultHref="/tabs/schedule"></IonBackButton>
|
||||
</IonButtons>
|
||||
<IonButtons slot="end">
|
||||
<IonButton onClick={() => {}}>
|
||||
{false ? (
|
||||
<IonIcon slot="icon-only" icon={star}></IonIcon>
|
||||
) : (
|
||||
<IonIcon slot="icon-only" icon={starOutline}></IonIcon>
|
||||
)}
|
||||
</IonButton>
|
||||
<IonButton onClick={() => {}}>
|
||||
<IonIcon slot="icon-only" icon={share}></IonIcon>
|
||||
</IonButton>
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent>
|
||||
{/* helloworld */}
|
||||
<Swiper>
|
||||
<SwiperSlide>
|
||||
<div
|
||||
style={{
|
||||
width: '100vw',
|
||||
height: '33vh',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
backgroundImage: `url(//plus.unsplash.com/premium_photo-1661964071015-d97428970584)`,
|
||||
}}
|
||||
></div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div
|
||||
style={{
|
||||
width: '100vw',
|
||||
height: '33vh',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
backgroundImage: `url(//images.unsplash.com/photo-1618773928121-c32242e63f39)`,
|
||||
}}
|
||||
></div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div
|
||||
style={{
|
||||
width: '100vw',
|
||||
height: '33vh',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
backgroundImage: `url(//images.unsplash.com/photo-1551882547-ff40c63fe5fa)`,
|
||||
}}
|
||||
></div>
|
||||
</SwiperSlide>
|
||||
</Swiper>
|
||||
|
||||
{/* */}
|
||||
<div
|
||||
id="content"
|
||||
style={{
|
||||
paddingLeft: '0.5rem',
|
||||
paddingRight: '0.5rem',
|
||||
marginTop: '1rem',
|
||||
marginBottom: '1rem',
|
||||
}}
|
||||
>
|
||||
<div dangerouslySetInnerHTML={{ __html: htmlContent }}>{}</div>
|
||||
</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect<OwnProps, {}, DispatchProps>({
|
||||
mapDispatchToProps: {
|
||||
setHasSeenTutorial,
|
||||
setMenuEnabled,
|
||||
},
|
||||
component: HotelServiceWifi,
|
||||
});
|
BIN
03_source/mobile/src/pages/HotelServiceWifi/product.png
Normal file
After Width: | Height: | Size: 180 KiB |
BIN
03_source/mobile/src/pages/HotelServiceWifi/profile.png
Normal file
After Width: | Height: | Size: 123 KiB |
BIN
03_source/mobile/src/pages/HotelServiceWifi/sampleHeader.png
Normal file
After Width: | Height: | Size: 976 KiB |
BIN
03_source/mobile/src/pages/HotelServiceWifi/schedule.png
Normal file
After Width: | Height: | Size: 80 KiB |
56
03_source/mobile/src/pages/HotelServiceWifi/style.scss
Normal file
@@ -0,0 +1,56 @@
|
||||
#tutorial-page {
|
||||
ion-toolbar {
|
||||
--background: transparent;
|
||||
--border-color: transparent;
|
||||
}
|
||||
|
||||
.slide-title {
|
||||
margin-top: 2.8rem;
|
||||
}
|
||||
|
||||
.slider {
|
||||
display: grid;
|
||||
// grid-template-columns: repeat(4, 100%);
|
||||
grid-template-rows: 1fr;
|
||||
|
||||
height: 100%;
|
||||
|
||||
overflow: scroll;
|
||||
scroll-snap-type: x mandatory;
|
||||
}
|
||||
|
||||
section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
|
||||
width: 100%;
|
||||
|
||||
scroll-snap-align: center;
|
||||
scroll-snap-stop: always;
|
||||
}
|
||||
|
||||
.slide-image {
|
||||
max-height: 50%;
|
||||
max-width: 60%;
|
||||
margin: -5vh 0 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
b {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
p {
|
||||
padding: 0 40px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: var(--ion-color-step-600, #60646b);
|
||||
|
||||
b {
|
||||
color: var(--ion-text-color, #000000);
|
||||
}
|
||||
}
|
||||
}
|
155
03_source/mobile/src/pages/HotelWelcomeTour/index.tsx
Normal file
@@ -0,0 +1,155 @@
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import {
|
||||
IonContent,
|
||||
IonPage,
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonButtons,
|
||||
IonButton,
|
||||
IonIcon,
|
||||
useIonViewWillEnter,
|
||||
} from '@ionic/react';
|
||||
import { arrowForward } from 'ionicons/icons';
|
||||
import { setMenuEnabled } from '../../data/sessions/sessions.actions';
|
||||
import { setHasSeenTutorial } from '../../data/user/user.actions';
|
||||
import './style.scss';
|
||||
import { connect } from '../../data/connect';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import PATHS from '../../PATHS';
|
||||
import getUnsplashRandomImage from '../../api/getUnsplashRandomImage';
|
||||
import schedulePng from './sampleHeader.png';
|
||||
|
||||
interface OwnProps extends RouteComponentProps {}
|
||||
interface DispatchProps {
|
||||
setHasSeenTutorial: typeof setHasSeenTutorial;
|
||||
setMenuEnabled: typeof setMenuEnabled;
|
||||
}
|
||||
|
||||
interface TutorialProps extends OwnProps, DispatchProps {}
|
||||
|
||||
const HotelWelcomeTour: React.FC<TutorialProps> = ({
|
||||
history,
|
||||
setHasSeenTutorial,
|
||||
setMenuEnabled,
|
||||
}) => {
|
||||
const sliderRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useIonViewWillEnter(() => {
|
||||
setMenuEnabled(false);
|
||||
// Scroll to first slide when entering the tutorial
|
||||
if (sliderRef.current) {
|
||||
sliderRef.current.scrollTo({
|
||||
left: 0,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const startApp = async () => {
|
||||
await setHasSeenTutorial(true);
|
||||
await setMenuEnabled(true);
|
||||
|
||||
history.push(PATHS.EVENT_LIST, { direction: 'none' });
|
||||
};
|
||||
|
||||
return (
|
||||
<IonPage id="tutorial-page">
|
||||
<IonHeader className="ion-no-border">
|
||||
<IonToolbar>
|
||||
<IonButtons slot="end">
|
||||
<IonButton color="primary" onClick={startApp}>
|
||||
Skip
|
||||
</IonButton>
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
{/* */}
|
||||
<IonContent fullscreen>
|
||||
<div
|
||||
className="slider"
|
||||
ref={sliderRef}
|
||||
style={{
|
||||
gridTemplateColumns: `repeat(4, 100%)`,
|
||||
}}
|
||||
>
|
||||
{/* */}
|
||||
<section>
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
height: 'calc( 100vh - 70px )',
|
||||
backgroundImage: `url(https://images.unsplash.com/photo-1549294413-26f195200c16)`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
}}
|
||||
></div>
|
||||
<div
|
||||
style={{
|
||||
color: 'white',
|
||||
position: 'relative',
|
||||
top: '-500px',
|
||||
height: '0',
|
||||
}}
|
||||
>
|
||||
<h2 className="slide-title">ホテルの利益率は?</h2>
|
||||
<p style={{ color: 'white' }}>
|
||||
新型コロナウイルス感染症拡大により、ホテル業界は厳しい経営を強いられる中、ウィズコロナに向けて。
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
{/* */}
|
||||
<section>
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
height: 'calc( 100vh - 70px )',
|
||||
backgroundImage: `url(https://images.unsplash.com/photo-1482049016688-2d3e1b311543)`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
}}
|
||||
></div>
|
||||
<div style={{ color: 'white', position: 'relative', top: '-500px', height: '0' }}>
|
||||
<h2 className="slide-title">ホテルの利益率は?</h2>
|
||||
<p style={{ color: 'white' }}>
|
||||
新型コロナウイルス感染症拡大により、ホテル業界は厳しい経営を強いられる中、ウィズコロナに向けて。
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
{/* */}
|
||||
|
||||
<section>
|
||||
<div className="swiper-item">
|
||||
<img src={schedulePng} alt="" className="slide-image" />
|
||||
<h2 className="slide-title">飲食部門</h2>
|
||||
<p>
|
||||
ホテルに内設されているレストランやバーで、飲食サービスを提供するのが飲食部門です。
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
{/* */}
|
||||
<section>
|
||||
<div className="swiper-item">
|
||||
<img src="assets/img/ica-slidebox-img-4.png" alt="" className="slide-image" />
|
||||
<h2 className="slide-title">宴会・イベント部門</h2>
|
||||
<p>
|
||||
団体・グループで宴会やイベントを催したい場合に、会場の貸し出しや料理の提供を行うのが宴会・イベント部門です。
|
||||
</p>
|
||||
<IonButton fill="clear" onClick={startApp}>
|
||||
Continue
|
||||
<IonIcon slot="end" icon={arrowForward} />
|
||||
</IonButton>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect<OwnProps, {}, DispatchProps>({
|
||||
mapDispatchToProps: {
|
||||
setHasSeenTutorial,
|
||||
setMenuEnabled,
|
||||
},
|
||||
component: HotelWelcomeTour,
|
||||
});
|
BIN
03_source/mobile/src/pages/HotelWelcomeTour/product.png
Normal file
After Width: | Height: | Size: 180 KiB |
BIN
03_source/mobile/src/pages/HotelWelcomeTour/profile.png
Normal file
After Width: | Height: | Size: 123 KiB |
BIN
03_source/mobile/src/pages/HotelWelcomeTour/sampleHeader.png
Normal file
After Width: | Height: | Size: 976 KiB |
BIN
03_source/mobile/src/pages/HotelWelcomeTour/schedule.png
Normal file
After Width: | Height: | Size: 80 KiB |
56
03_source/mobile/src/pages/HotelWelcomeTour/style.scss
Normal file
@@ -0,0 +1,56 @@
|
||||
#tutorial-page {
|
||||
ion-toolbar {
|
||||
--background: transparent;
|
||||
--border-color: transparent;
|
||||
}
|
||||
|
||||
.slide-title {
|
||||
margin-top: 2.8rem;
|
||||
}
|
||||
|
||||
.slider {
|
||||
display: grid;
|
||||
// grid-template-columns: repeat(4, 100%);
|
||||
grid-template-rows: 1fr;
|
||||
|
||||
height: 100%;
|
||||
|
||||
overflow: scroll;
|
||||
scroll-snap-type: x mandatory;
|
||||
}
|
||||
|
||||
section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
|
||||
width: 100%;
|
||||
|
||||
scroll-snap-align: center;
|
||||
scroll-snap-stop: always;
|
||||
}
|
||||
|
||||
.slide-image {
|
||||
max-height: 50%;
|
||||
max-width: 60%;
|
||||
margin: -5vh 0 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
b {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
p {
|
||||
padding: 0 40px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: var(--ion-color-step-600, #60646b);
|
||||
|
||||
b {
|
||||
color: var(--ion-text-color, #000000);
|
||||
}
|
||||
}
|
||||
}
|
1
03_source/mobile/src/pages/Insights/buy_sell.svg
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
03_source/mobile/src/pages/Insights/histogram.jpg
Normal file
After Width: | Height: | Size: 144 KiB |
124
03_source/mobile/src/pages/Insights/index.tsx
Normal file
@@ -0,0 +1,124 @@
|
||||
import {
|
||||
IonBackButton,
|
||||
IonButton,
|
||||
IonButtons,
|
||||
IonContent,
|
||||
IonHeader,
|
||||
IonIcon,
|
||||
IonPage,
|
||||
IonPopover,
|
||||
IonSegment,
|
||||
IonSegmentButton,
|
||||
IonText,
|
||||
IonTitle,
|
||||
IonToolbar,
|
||||
} from '@ionic/react';
|
||||
import { star, starOutline } from 'ionicons/icons';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
// import AboutPopover from '../../components/AboutPopover';
|
||||
|
||||
import './style.scss';
|
||||
import histogramJpg from './histogram.jpg';
|
||||
import buySellSvg from './buy_sell.svg';
|
||||
|
||||
type SessionListProps = {
|
||||
hide: boolean;
|
||||
};
|
||||
|
||||
const ShoutoutContent: React.FC<SessionListProps> = ({ hide }) => {
|
||||
if (hide) return <></>;
|
||||
return (
|
||||
<>
|
||||
<div style={{ margin: '3rem 1rem 0 1rem' }}>
|
||||
<IonText>
|
||||
<h6>Create a shoutout to reach more buyers</h6>
|
||||
</IonText>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-start',
|
||||
gap: '3rem',
|
||||
}}
|
||||
>
|
||||
<IonText color={'tertiary'} style={{ fontSize: '0.8rem' }}>
|
||||
der of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it
|
||||
is pleasure, but because those who do not know how to pursue pleasure rationally
|
||||
encounter consequences that are extremely painful. Nor again is there
|
||||
</IonText>
|
||||
<img src={buySellSvg} width="30%" height="auto" style={{ minWidth: '100px' }} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const VisitsContent: React.FC<SessionListProps> = ({ hide }) => {
|
||||
if (hide) return <></>;
|
||||
return (
|
||||
<>
|
||||
<img src={histogramJpg} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
interface InsightsProps {}
|
||||
|
||||
const Insights: React.FC<InsightsProps> = () => {
|
||||
const [showPopover, setShowPopover] = useState(false);
|
||||
const [popoverEvent, setPopoverEvent] = useState<MouseEvent>();
|
||||
const [location, setLocation] = useState<'madison' | 'austin' | 'chicago' | 'seattle'>('madison');
|
||||
const [conferenceDate, setConferenceDate] = useState('2047-05-17T00:00:00-05:00');
|
||||
|
||||
const [segment, setSegment] = useState<'visits' | 'shoutout'>('visits');
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<IonPage id="insights-page">
|
||||
<IonHeader translucent={true} className="ion-no-border">
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonBackButton defaultHref="/tabs/schedule"></IonBackButton>
|
||||
</IonButtons>
|
||||
|
||||
<IonButtons slot="end">
|
||||
<IonButton onClick={() => {}}>
|
||||
{false ? (
|
||||
<IonIcon slot="icon-only" icon={star}></IonIcon>
|
||||
) : (
|
||||
<IonIcon slot="icon-only" icon={starOutline}></IonIcon>
|
||||
)}
|
||||
</IonButton>
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent>
|
||||
<div style={{ marginLeft: '1rem', marginRight: '1rem' }}>
|
||||
<IonText>
|
||||
<h3>Insights</h3>
|
||||
</IonText>
|
||||
</div>{' '}
|
||||
<IonSegment
|
||||
mode={'md'}
|
||||
value={segment}
|
||||
onIonChange={(e) => setSegment(e.detail.value as any)}
|
||||
>
|
||||
<IonSegmentButton value="visits">{t('Visits')}</IonSegmentButton>
|
||||
<IonSegmentButton value="shoutout">{t('Shoutout')}</IonSegmentButton>
|
||||
</IonSegment>
|
||||
<VisitsContent hide={segment != 'visits'} />
|
||||
<ShoutoutContent hide={segment != 'shoutout'} />
|
||||
</IonContent>
|
||||
|
||||
{/*
|
||||
<IonPopover isOpen={showPopover} event={popoverEvent} onDidDismiss={() => setShowPopover(false)}>
|
||||
<AboutPopover dismiss={() => setShowPopover(false)} />
|
||||
</IonPopover>
|
||||
*/}
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Insights);
|
2
03_source/mobile/src/pages/Insights/style.scss
Normal file
@@ -0,0 +1,2 @@
|
||||
#sample-blank-page {
|
||||
}
|
@@ -65,24 +65,29 @@ import svgIcon36 from './icons/holiday-hotel-motel-sign-travel-svgrepo-com.svg';
|
||||
import svgIcon37 from './icons/holiday-journey-luggage-suitcase-vacation-2-svgrepo-com.svg';
|
||||
import svgIcon38 from './icons/hotel-location-map-pin-travel-svgrepo-com.svg';
|
||||
import getButtonSvg from '../../api/getButtonSvg';
|
||||
import HotelServiceIntro from '../HotelWelcomeTour';
|
||||
import HotelIntro from '../HotelIntro';
|
||||
import HotelServiceWifi from '../HotelServiceIntro';
|
||||
import OrderHistory from '../OrderHistory';
|
||||
import Insights from '../Insights';
|
||||
//
|
||||
|
||||
const hotelServiceMenu = [
|
||||
{ name: '停車場', icon: svgIcon1, targetUrl: '/tabs/hotel_service_intro' },
|
||||
{ name: '接機服務', icon: svgIcon2, targetUrl: '/tabs/hotel_service_intro' },
|
||||
{ name: '停車場', icon: svgIcon1, targetUrl: PATHS.HOTEL_WELCOME_TOUR },
|
||||
{ name: '接機服務', icon: svgIcon2, targetUrl: PATHS.HOTEL_WELCOME_TOUR },
|
||||
{ name: '餐廳', icon: svgIcon3, targetUrl: '/tabs/hotel/:hotelid/restaurants' },
|
||||
{ name: '咖啡室', icon: svgIcon4, targetUrl: '/tabs/restaurant/:restaurant_id/food/:food_id/intro' },
|
||||
{ name: '酒吧', icon: svgIcon5, targetUrl: '/tabs/hotel_service_intro' },
|
||||
{ name: '會議室', icon: svgIcon6, targetUrl: '/tabs/hotel_service_intro' },
|
||||
{ name: '托兒服務', icon: svgIcon7, targetUrl: '/tabs/hotel_service_intro' },
|
||||
{ name: '茶室', icon: svgIcon8, targetUrl: '/tabs/hotel_service_intro' },
|
||||
{ name: '多功能室', icon: svgIcon9, targetUrl: '/tabs/hotel_service_intro' },
|
||||
{ name: '商務便利設施', icon: svgIcon10, targetUrl: '/tabs/hotel_service_intro' },
|
||||
{ name: '健身室', icon: svgIcon11, targetUrl: '/tabs/hotel_service_intro' },
|
||||
{ name: '公共區域 Wi-Fi免費', icon: svgIcon12, targetUrl: '/tabs/hotel_service_intro' },
|
||||
{ name: '貨幣兌換', icon: svgIcon13, targetUrl: '/tabs/hotel_service_intro' },
|
||||
{ name: '行李寄存免費', icon: svgIcon14, targetUrl: '/tabs/hotel_service_intro' },
|
||||
{ name: '叫醒服務', icon: svgIcon16, targetUrl: '/tabs/hotel_service_intro' },
|
||||
{ name: '酒吧', icon: svgIcon5, targetUrl: PATHS.HOTEL_WELCOME_TOUR },
|
||||
{ name: '會議室', icon: svgIcon6, targetUrl: PATHS.HOTEL_WELCOME_TOUR },
|
||||
{ name: '托兒服務', icon: svgIcon7, targetUrl: PATHS.HOTEL_WELCOME_TOUR },
|
||||
{ name: '茶室', icon: svgIcon8, targetUrl: PATHS.HOTEL_WELCOME_TOUR },
|
||||
{ name: '多功能室', icon: svgIcon9, targetUrl: PATHS.HOTEL_WELCOME_TOUR },
|
||||
{ name: '商務便利設施', icon: svgIcon10, targetUrl: PATHS.HOTEL_WELCOME_TOUR },
|
||||
{ name: '健身室', icon: svgIcon11, targetUrl: PATHS.HOTEL_WELCOME_TOUR },
|
||||
{ name: '公共區域 Wi-Fi免費', icon: svgIcon12, targetUrl: PATHS.HOTEL_WELCOME_TOUR },
|
||||
{ name: '貨幣兌換', icon: svgIcon13, targetUrl: PATHS.HOTEL_WELCOME_TOUR },
|
||||
{ name: 'wifi', icon: svgIcon14, targetUrl: PATHS.HOTEL_SERVICE_WIFI },
|
||||
{ name: '叫醒服務', icon: svgIcon16, targetUrl: PATHS.HOTEL_WELCOME_TOUR },
|
||||
{ name: '關於酒店', icon: svgIcon16, targetUrl: '/tabs/hotel_intro' },
|
||||
];
|
||||
|
||||
@@ -136,13 +141,18 @@ const MainTabs: React.FC<MainTabsProps> = () => {
|
||||
{/* */}
|
||||
<Route path={PATHS.CAROUSELL_ME} render={() => <CarousellMe />} exact={true} />
|
||||
<Route path={PATHS.CAROUSELL_ME_QR} component={QRPage} exact={true} />
|
||||
<Route path={PATHS.CAROUSELL_ME_INSIGHTS} component={Insights} exact={true} />
|
||||
{/*
|
||||
<Route path="/tabs/carousell_me/insights" component={Insights} exact={true} />
|
||||
<Route path="/tabs/carousell_me/OffersMade" component={OffersMade} exact={true} />
|
||||
<Route path="/tabs/carousell_me/settings" component={Settings} exact={true} />
|
||||
<Route path="/tabs/carousell_me/my_profile" component={MyProfile} exact={true} />
|
||||
*/}
|
||||
|
||||
<Route path={PATHS.HOTEL_INTRO} component={HotelIntro} exact={true} />
|
||||
<Route path={PATHS.HOTEL_SERVICE_WIFI} component={HotelServiceWifi} exact={true} />
|
||||
|
||||
<Route path="/tabs/restaurant/:restaurant_id/order_history" render={() => <OrderHistory />} exact={true} />
|
||||
|
||||
<TabAppRoute />
|
||||
</IonRouterOutlet>
|
||||
|
||||
|
@@ -0,0 +1,71 @@
|
||||
import { IonItem, IonText } from '@ionic/react';
|
||||
import React from 'react';
|
||||
|
||||
interface OrderHistoryItemProps {
|
||||
status: string;
|
||||
quantity: number;
|
||||
price: number;
|
||||
name: string;
|
||||
time: string;
|
||||
}
|
||||
|
||||
const OrderHistoryItem: React.FC<OrderHistoryItemProps> = ({
|
||||
status,
|
||||
quantity,
|
||||
price,
|
||||
name,
|
||||
time,
|
||||
}) => {
|
||||
let statusBgColor = ['rgba(128,0,0,0.1)', 'rgba(0,128,0,0.1)', 'rgba(0,0,128,0.1)'];
|
||||
let statusTextcolor = ['rgba(128,0,0,0.9)', 'rgba(0,128,0,0.9)', 'rgba(0,0,128,0.9)'];
|
||||
let statusMap = ['準備中', '提供済', 'キャンセル'];
|
||||
let statusIdx = statusMap.indexOf(status) || 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
<IonItem lines="full" slot="fixed" style={{ width: '100%', height: '100%' }}>
|
||||
<div style={{ width: '80px' }}>
|
||||
<IonText
|
||||
color="danger"
|
||||
style={{
|
||||
backgroundColor: statusBgColor[statusIdx],
|
||||
color: statusTextcolor[statusIdx],
|
||||
borderRadius: '3px',
|
||||
fontSize: '0.8rem',
|
||||
padding: '5px',
|
||||
minWidth: '80px',
|
||||
}}
|
||||
>
|
||||
{status}
|
||||
</IonText>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
width: 'calc( 100% - 80px )',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<IonText>{name}</IonText>
|
||||
<div style={{ fontSize: '0.8rem', color: 'grey' }}>受付: {time}</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<IonText>
|
||||
¥{price} x {quantity}点
|
||||
</IonText>
|
||||
</div>
|
||||
</div>
|
||||
</IonItem>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(OrderHistoryItem);
|
229
03_source/mobile/src/pages/OrderHistory/index.tsx
Normal file
@@ -0,0 +1,229 @@
|
||||
import {
|
||||
IonAvatar,
|
||||
IonBackButton,
|
||||
IonButton,
|
||||
IonButtons,
|
||||
IonCard,
|
||||
IonCardContent,
|
||||
IonCardHeader,
|
||||
IonCardSubtitle,
|
||||
IonCardTitle,
|
||||
IonChip,
|
||||
IonContent,
|
||||
IonHeader,
|
||||
IonIcon,
|
||||
IonItem,
|
||||
IonLabel,
|
||||
IonList,
|
||||
IonPage,
|
||||
IonText,
|
||||
IonTitle,
|
||||
IonToolbar,
|
||||
useIonRouter,
|
||||
} from '@ionic/react';
|
||||
import React from 'react';
|
||||
import { Virtuoso } from 'react-virtuoso';
|
||||
|
||||
import { arrowBackCircleOutline, share, star, starOutline } from 'ionicons/icons';
|
||||
import './style.scss';
|
||||
import OrderHistoryItem from './OrderHistoryItem';
|
||||
|
||||
const orderList = [
|
||||
{
|
||||
status: '準備中',
|
||||
quantity: 1,
|
||||
price: 1,
|
||||
name: 'テスト',
|
||||
time: '23年02月08日 16時43分',
|
||||
},
|
||||
|
||||
{
|
||||
status: '提供済',
|
||||
quantity: 1,
|
||||
price: 1,
|
||||
name: 'テスト',
|
||||
time: '23年02月08日 16時43分',
|
||||
},
|
||||
|
||||
{
|
||||
status: 'キャンセル',
|
||||
quantity: 1,
|
||||
price: 1,
|
||||
name: 'テスト',
|
||||
time: '23年02月08日 16時43分',
|
||||
},
|
||||
];
|
||||
|
||||
interface OrderHistoryProps {}
|
||||
|
||||
const OrderHistory: React.FC<OrderHistoryProps> = () => {
|
||||
const route = useIonRouter();
|
||||
const test = () => {
|
||||
console.log(route);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<IonPage>
|
||||
<IonHeader className="ion-no-border">
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonBackButton text="" defaultHref="/tabs/schedule"></IonBackButton>
|
||||
</IonButtons>
|
||||
<IonText>注文履歴</IonText>
|
||||
<IonButtons slot="end">
|
||||
<IonButton onClick={() => {}}>
|
||||
{false ? (
|
||||
<IonIcon slot="icon-only" icon={star}></IonIcon>
|
||||
) : (
|
||||
<IonIcon slot="icon-only" icon={starOutline}></IonIcon>
|
||||
)}
|
||||
</IonButton>
|
||||
<IonButton onClick={() => {}}>
|
||||
<IonIcon slot="icon-only" icon={share}></IonIcon>
|
||||
</IonButton>
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent>
|
||||
<div
|
||||
style={{
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<IonCard
|
||||
slot="fixed"
|
||||
style={{ top: '0.5rem', height: 'calc( 100% - 30% - 3rem )', flexGrow: 1 }}
|
||||
>
|
||||
<IonCardContent
|
||||
style={{
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
}}
|
||||
>
|
||||
<IonText style={{ padding: '1rem', fontSize: '1.1rem', fontWeight: 'bold' }}>
|
||||
已 order 清單
|
||||
</IonText>
|
||||
<Virtuoso
|
||||
style={{ flexGrow: 1, height: '100%' }}
|
||||
totalCount={100}
|
||||
itemContent={(index) => {
|
||||
return (
|
||||
<>
|
||||
<OrderHistoryItem
|
||||
status={'準備中'}
|
||||
quantity={1}
|
||||
price={1}
|
||||
name={'テスト'}
|
||||
time={'23年02月08日 16時43分'}
|
||||
/>
|
||||
|
||||
<OrderHistoryItem
|
||||
status={'提供済'}
|
||||
quantity={1}
|
||||
price={1}
|
||||
name={'テスト'}
|
||||
time={'23年02月08日 16時43分'}
|
||||
/>
|
||||
|
||||
<OrderHistoryItem
|
||||
status={'キャンセル'}
|
||||
quantity={1}
|
||||
price={1}
|
||||
name={'テスト'}
|
||||
time={'23年02月08日 16時43分'}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</IonCardContent>
|
||||
</IonCard>
|
||||
|
||||
<div>
|
||||
<IonCard slot="fixed" style={{ bottom: '0.5rem', left: '0.1rem', right: '0.1rem' }}>
|
||||
<IonCardContent>
|
||||
<div style={{ opacity: 0.9 }}>
|
||||
<div
|
||||
style={{ display: 'flex', flexDirection: 'row', justifyContent: 'flex-end' }}
|
||||
>
|
||||
<div>
|
||||
<IonText
|
||||
style={{ color: '#16a085', fontSize: '1.3rem', fontWeight: 'bold' }}
|
||||
>
|
||||
合計
|
||||
</IonText>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
width: '45%',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
>
|
||||
<IonText
|
||||
style={{ color: '#16a085', fontSize: '1.3rem', fontWeight: 'bold' }}
|
||||
>
|
||||
¥1000
|
||||
</IonText>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{ display: 'flex', flexDirection: 'row', justifyContent: 'flex-end' }}
|
||||
>
|
||||
<div>
|
||||
<IonText style={{ color: '#2c3e50', fontSize: '1rem', fontWeight: 'bold' }}>
|
||||
割り勘する
|
||||
</IonText>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
width: '45%',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
>
|
||||
1
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{ display: 'flex', flexDirection: 'row', justifyContent: 'flex-end' }}
|
||||
>
|
||||
<div>
|
||||
<IonText style={{ color: '#2c3e50', fontSize: '1rem', fontWeight: 'bold' }}>
|
||||
合計
|
||||
</IonText>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
color: '#2c3e50',
|
||||
width: '45%',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
>
|
||||
一人当たり ¥1
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</IonCardContent>
|
||||
</IonCard>
|
||||
</div>
|
||||
</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(OrderHistory);
|
2
03_source/mobile/src/pages/OrderHistory/style.scss
Normal file
@@ -0,0 +1,2 @@
|
||||
#order-history-page {
|
||||
}
|
@@ -1,106 +0,0 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
IonContent,
|
||||
IonPage,
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonButtons,
|
||||
IonButton,
|
||||
IonIcon,
|
||||
useIonViewWillEnter,
|
||||
} from '@ionic/react';
|
||||
import { arrowForward } from 'ionicons/icons';
|
||||
import { setMenuEnabled } from '../data/sessions/sessions.actions';
|
||||
import { setHasSeenTutorial } from '../data/user/user.actions';
|
||||
import './Tutorial.scss';
|
||||
import { connect } from '../data/connect';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
|
||||
interface OwnProps extends RouteComponentProps {}
|
||||
interface DispatchProps {
|
||||
setHasSeenTutorial: typeof setHasSeenTutorial;
|
||||
setMenuEnabled: typeof setMenuEnabled;
|
||||
}
|
||||
|
||||
interface TutorialProps extends OwnProps, DispatchProps {}
|
||||
|
||||
const Tutorial: React.FC<TutorialProps> = ({ history, setHasSeenTutorial, setMenuEnabled }) => {
|
||||
useIonViewWillEnter(() => {
|
||||
setMenuEnabled(false);
|
||||
});
|
||||
|
||||
const startApp = async () => {
|
||||
await setHasSeenTutorial(true);
|
||||
await setMenuEnabled(true);
|
||||
history.push('/tabs/schedule', { direction: 'none' });
|
||||
};
|
||||
|
||||
return (
|
||||
<IonPage id="tutorial-page">
|
||||
<IonHeader no-border>
|
||||
<IonToolbar>
|
||||
<IonButtons slot="end">
|
||||
<IonButton color="primary" onClick={startApp}>
|
||||
Skip
|
||||
</IonButton>
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<IonContent fullscreen>
|
||||
<div className="slider">
|
||||
<section>
|
||||
<div className="swiper-item">
|
||||
<img src="assets/img/ica-slidebox-img-1.png" alt="" className="slide-image" />
|
||||
<h2 className="slide-title">
|
||||
Welcome to <b>ICA</b>
|
||||
</h2>
|
||||
<p>
|
||||
The <b>ionic conference app</b> is a practical preview of the ionic framework in
|
||||
action, and a demonstration of proper code use.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div className="swiper-item">
|
||||
<img src="assets/img/ica-slidebox-img-2.png" alt="" className="slide-image" />
|
||||
<h2 className="slide-title">What is Ionic?</h2>
|
||||
<p>
|
||||
<b>Ionic Framework</b> is an open source SDK that enables developers to build high
|
||||
quality mobile apps with web technologies like HTML, CSS, and JavaScript.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div className="swiper-item">
|
||||
<img src="assets/img/ica-slidebox-img-3.png" alt="" className="slide-image" />
|
||||
<h2 className="slide-title">What is Ionic Appflow?</h2>
|
||||
<p>
|
||||
<b>Ionic Appflow</b> is a powerful set of services and features built on top of
|
||||
Ionic Framework that brings a totally new level of app development agility to mobile
|
||||
dev teams.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div className="swiper-item">
|
||||
<img src="assets/img/ica-slidebox-img-4.png" alt="" className="slide-image" />
|
||||
<h2 className="slide-title">Ready to Play?</h2>
|
||||
<IonButton fill="clear" onClick={startApp}>
|
||||
Continue
|
||||
<IonIcon slot="end" icon={arrowForward} />
|
||||
</IonButton>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect<OwnProps, {}, DispatchProps>({
|
||||
mapDispatchToProps: {
|
||||
setHasSeenTutorial,
|
||||
setMenuEnabled,
|
||||
},
|
||||
component: Tutorial,
|
||||
});
|
@@ -1,115 +0,0 @@
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import {
|
||||
IonContent,
|
||||
IonPage,
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonButtons,
|
||||
IonButton,
|
||||
IonIcon,
|
||||
useIonViewWillEnter,
|
||||
} from '@ionic/react';
|
||||
import { arrowForward } from 'ionicons/icons';
|
||||
import { setMenuEnabled } from '../../data/sessions/sessions.actions';
|
||||
import { setHasSeenTutorial } from '../../data/user/user.actions';
|
||||
import './Tutorial.scss';
|
||||
import { connect } from '../../data/connect';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
|
||||
interface OwnProps extends RouteComponentProps {}
|
||||
interface DispatchProps {
|
||||
setHasSeenTutorial: typeof setHasSeenTutorial;
|
||||
setMenuEnabled: typeof setMenuEnabled;
|
||||
}
|
||||
|
||||
interface TutorialProps extends OwnProps, DispatchProps {}
|
||||
|
||||
const Tutorial: React.FC<TutorialProps> = ({ history, setHasSeenTutorial, setMenuEnabled }) => {
|
||||
const sliderRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useIonViewWillEnter(() => {
|
||||
setMenuEnabled(false);
|
||||
// Scroll to first slide when entering the tutorial
|
||||
if (sliderRef.current) {
|
||||
sliderRef.current.scrollTo({
|
||||
left: 0,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const startApp = async () => {
|
||||
await setHasSeenTutorial(true);
|
||||
await setMenuEnabled(true);
|
||||
history.push('/tabs/schedule', { direction: 'none' });
|
||||
};
|
||||
|
||||
return (
|
||||
<IonPage id="tutorial-page">
|
||||
<IonHeader className="ion-no-border">
|
||||
<IonToolbar>
|
||||
<IonButtons slot="end">
|
||||
<IonButton color="primary" onClick={startApp}>
|
||||
Skip
|
||||
</IonButton>
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<IonContent fullscreen>
|
||||
<div className="slider" ref={sliderRef}>
|
||||
<section>
|
||||
<div className="swiper-item">
|
||||
<img src="assets/img/ica-slidebox-img-1.png" alt="" className="slide-image" />
|
||||
<h2 className="slide-title">
|
||||
Welcome to <b>ICA</b>
|
||||
</h2>
|
||||
<p>
|
||||
The <b>ionic conference app</b> is a practical preview of the ionic framework in
|
||||
action, and a demonstration of proper code use.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div className="swiper-item">
|
||||
<img src="assets/img/ica-slidebox-img-2.png" alt="" className="slide-image" />
|
||||
<h2 className="slide-title">What is Ionic?</h2>
|
||||
<p>
|
||||
<b>Ionic Framework</b> is an open source SDK that enables developers to build high
|
||||
quality mobile apps with web technologies like HTML, CSS, and JavaScript.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div className="swiper-item">
|
||||
<img src="assets/img/ica-slidebox-img-3.png" alt="" className="slide-image" />
|
||||
<h2 className="slide-title">What is Ionic Appflow?</h2>
|
||||
<p>
|
||||
<b>Ionic Appflow</b> is a powerful set of services and features built on top of
|
||||
Ionic Framework that brings a totally new level of app development agility to mobile
|
||||
dev teams.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div className="swiper-item">
|
||||
<img src="assets/img/ica-slidebox-img-4.png" alt="" className="slide-image" />
|
||||
<h2 className="slide-title">Ready to Play?</h2>
|
||||
<IonButton fill="clear" onClick={startApp}>
|
||||
Continue
|
||||
<IonIcon slot="end" icon={arrowForward} />
|
||||
</IonButton>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect<OwnProps, {}, DispatchProps>({
|
||||
mapDispatchToProps: {
|
||||
setHasSeenTutorial,
|
||||
setMenuEnabled,
|
||||
},
|
||||
component: Tutorial,
|
||||
});
|
@@ -20,9 +20,6 @@
|
||||
},
|
||||
{
|
||||
"path": "98_AI_workspace"
|
||||
},
|
||||
{
|
||||
"path": "99_references"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
|