├── .editorconfig
├── .gitignore
├── .vscode
└── extensions.json
├── LICENSE
├── README.md
├── client
├── .env.example
├── .eslintrc.cjs
├── README.md
├── index.html
├── package.json
├── postcss.config.js
├── public
│ ├── pics
│ │ ├── _hotel-1.jpg
│ │ ├── _hotel-2.jpg
│ │ ├── _room-1-1.jpg
│ │ ├── _room-1-2.jpg
│ │ ├── hotel-1.jpg
│ │ ├── hotel-2.jpg
│ │ ├── hotel-3.jpg
│ │ ├── hotel-4.jpg
│ │ ├── hotel-5.jpg
│ │ ├── hotel-6.jpg
│ │ ├── hotel-7.jpg
│ │ ├── room-1-1.jpg
│ │ ├── room-1-2.jpg
│ │ ├── room-2-1.jpg
│ │ ├── room-2-2.jpg
│ │ ├── room-2-3.jpg
│ │ ├── room-3-1.jpg
│ │ ├── room-3-2.jpg
│ │ ├── room-3-3.jpg
│ │ ├── room-4-1.jpg
│ │ ├── room-4-2.jpg
│ │ ├── room-4-3.jpg
│ │ ├── room-5-1.jpg
│ │ ├── room-5-2.jpg
│ │ ├── room-6-1.jpg
│ │ ├── room-6-2.jpg
│ │ ├── room-6-3.jpg
│ │ ├── room-7-1.jpg
│ │ └── room-7-2.jpg
│ └── telebook.svg
├── src
│ ├── App.vue
│ ├── application
│ │ ├── router-shims.d.ts
│ │ ├── router.ts
│ │ └── services
│ │ │ ├── README.md
│ │ │ ├── index.ts
│ │ │ ├── useLayout.ts
│ │ │ ├── useLottie.ts
│ │ │ ├── useScroll.ts
│ │ │ ├── useTelegram.ts
│ │ │ └── useThumbnail.ts
│ ├── domain
│ │ ├── entities
│ │ │ ├── Award.ts
│ │ │ ├── Chart.ts
│ │ │ ├── City.ts
│ │ │ ├── EntityPicture.ts
│ │ │ ├── Hotel.ts
│ │ │ ├── LabeledPrice.ts
│ │ │ ├── Rating.ts
│ │ │ ├── Room.ts
│ │ │ ├── TripDetails.ts
│ │ │ ├── errors
│ │ │ │ ├── Base.ts
│ │ │ │ ├── NotFound.ts
│ │ │ │ └── Unauthorized.ts
│ │ │ └── index.ts
│ │ └── services
│ │ │ ├── README.md
│ │ │ ├── index.ts
│ │ │ ├── useCities.ts
│ │ │ ├── useHotel.ts
│ │ │ ├── useInvoice.ts
│ │ │ └── useTripDetails.ts
│ ├── infra
│ │ ├── store
│ │ │ ├── cities
│ │ │ │ ├── index.ts
│ │ │ │ └── mock
│ │ │ │ │ └── cities.ts
│ │ │ ├── hotels
│ │ │ │ └── mock
│ │ │ │ │ ├── amenities.ts
│ │ │ │ │ └── hotels.ts
│ │ │ ├── reviews
│ │ │ │ └── mock
│ │ │ │ │ └── reviews.ts
│ │ │ └── thumbs
│ │ │ │ ├── image.cache.ts
│ │ │ │ └── thumbs.json
│ │ ├── transport
│ │ │ ├── api
│ │ │ │ ├── index.ts
│ │ │ │ └── types
│ │ │ │ │ └── response.ts
│ │ │ ├── fetch
│ │ │ │ └── index.ts
│ │ │ └── types
│ │ │ │ └── json.ts
│ │ └── utils
│ │ │ ├── color.ts
│ │ │ ├── date.ts
│ │ │ ├── dom.ts
│ │ │ └── number.ts
│ ├── main.ts
│ ├── presentation
│ │ ├── assets
│ │ │ ├── fonts
│ │ │ │ ├── OpenSans.ttf
│ │ │ │ ├── Roboto-Black.ttf
│ │ │ │ ├── Roboto-Bold.ttf
│ │ │ │ ├── Roboto-Medium.ttf
│ │ │ │ └── Roboto-Regular.ttf
│ │ │ ├── icons
│ │ │ │ ├── arrow-left.svg
│ │ │ │ ├── arrow-right.svg
│ │ │ │ ├── card.svg
│ │ │ │ ├── checkmark.svg
│ │ │ │ ├── chevron-right.svg
│ │ │ │ ├── clock-fill.svg
│ │ │ │ ├── laurel-left.svg
│ │ │ │ ├── laurel-right.svg
│ │ │ │ ├── market-fill.svg
│ │ │ │ ├── search.svg
│ │ │ │ ├── settings-faq.svg
│ │ │ │ ├── settings-user.svg
│ │ │ │ ├── square-filled-air.svg
│ │ │ │ ├── square-filled-bed.svg
│ │ │ │ ├── square-filled-parking.svg
│ │ │ │ ├── square-filled-safe.svg
│ │ │ │ ├── square-filled-sport.svg
│ │ │ │ ├── square-filled-wifi.svg
│ │ │ │ ├── user-circle-fill.svg
│ │ │ │ └── xmark-24.svg
│ │ │ └── lottie
│ │ │ │ ├── eyes.json
│ │ │ │ └── simp.json
│ │ ├── components
│ │ │ ├── Amount
│ │ │ │ └── Amount.vue
│ │ │ ├── Avatar
│ │ │ │ └── Avatar.vue
│ │ │ ├── DataOverview
│ │ │ │ ├── DataOverview.vue
│ │ │ │ └── DataOverviewItem.vue
│ │ │ ├── DatePicker
│ │ │ │ ├── DatePicker.vue
│ │ │ │ └── DatePickerCompact.vue
│ │ │ ├── Icon
│ │ │ │ └── Icon.vue
│ │ │ ├── Input
│ │ │ │ └── Input.vue
│ │ │ ├── List
│ │ │ │ ├── List.vue
│ │ │ │ ├── ListCard.vue
│ │ │ │ ├── ListItem.vue
│ │ │ │ ├── ListItemExpandable.vue
│ │ │ │ └── ListItemIcon.vue
│ │ │ ├── Lottie
│ │ │ │ └── Lottie.vue
│ │ │ ├── Page
│ │ │ │ ├── FixedFooter.vue
│ │ │ │ └── WithHeader.vue
│ │ │ ├── Placeholder
│ │ │ │ └── Placeholder.vue
│ │ │ ├── README.md
│ │ │ ├── Rating
│ │ │ │ └── Rating.vue
│ │ │ ├── Section
│ │ │ │ ├── Section.vue
│ │ │ │ └── Sections.vue
│ │ │ ├── Text
│ │ │ │ └── Text.vue
│ │ │ └── index.ts
│ │ ├── screens
│ │ │ ├── Home.vue
│ │ │ ├── Hotel.vue
│ │ │ ├── Location.vue
│ │ │ └── Room.vue
│ │ └── styles
│ │ │ ├── hacks.css
│ │ │ ├── index.css
│ │ │ ├── reset.css
│ │ │ └── theme
│ │ │ ├── animations.css
│ │ │ ├── colors.css
│ │ │ ├── sizes.css
│ │ │ ├── spacings.css
│ │ │ └── typescale.css
│ └── vite-env.d.ts
├── tools
│ ├── README.md
│ └── thumbs.js
├── tsconfig.eslint.json
├── tsconfig.json
├── tsconfig.node.json
├── vercel.json
├── vite.config.ts
└── yarn.lock
├── docs
├── Awesome.md
├── Deployment.md
├── GetStarted.md
├── Payments.md
└── assets
│ ├── cover-light.png
│ ├── cover.png
│ ├── payments-showcase.png
│ └── ui
│ ├── Amount.png
│ ├── Avatar.png
│ ├── DataOverview.png
│ ├── DatePicker.png
│ ├── DatePickerCompact.png
│ ├── FixedFooter.png
│ ├── Icon.png
│ ├── Input.png
│ ├── List.png
│ ├── ListCard.png
│ ├── ListItem.png
│ ├── ListItemExpandable.png
│ ├── Lottie.png
│ ├── PageWithHeader.png
│ ├── Placeholder.png
│ ├── Rating.png
│ ├── Section.png
│ ├── Sections.png
│ └── Text.png
├── ngrok.example.yml
└── server
├── .env.example
├── .eslintrc.cjs
├── .gitignore
├── README.md
├── api
└── index.ts
├── package.json
├── public
└── privacy.html
├── src
├── api
│ ├── bot.ts
│ ├── http.ts
│ └── router
│ │ └── index.ts
├── config.ts
├── index.ts
└── infra
│ └── utils
│ └── notify
│ ├── index.ts
│ └── notify.ts
├── tsconfig.json
├── vercel.json
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | charset = utf-8
3 | indent_style = space
4 | end_of_line = lf
5 | insert_final_newline = true
6 | trim_trailing_whitespace = true
7 | indent_size = 2
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 | .env
26 | ngrok.yml
27 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
3 | }
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Peter Savchenko
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Telebook
2 |
3 | Telegram Mini App for booking hotels *
4 |
5 | * — it's a demonstration of [Telegram Mini Apps](https://core.telegram.org/bots/webapps) platform. No real hotels and payments.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | @tebook_bot/telebook |
19 | Telegram Mini Apps |
20 | Documentation
21 |
22 |
23 | Use this project as an example or template for the creation of your app:
24 |
25 | 1. 🧩 Meet Telegram Vue UI Kit — build native-like interfaces with ready-to-use components
26 | 3. ❤️🔥 Instant picture previews and on-device cache
27 | 4. ☘️ Smooth screen transitions
28 | 2. ✨ Advanced DX — fast build, hot reloading, modern code style and linters, well-documented code
29 | 5. 💎 Clean but simple architecture — easy to scale and maintain
30 | 6. 📦 Production-ready deployment setup
31 | 7. 💵 Payments support
32 | 8. 📋 Privacy Policy template
33 |
34 | ## 👋 About the example
35 |
36 | Telebook — is a kind of booking app that runs inside the Telegram. It provides several screens demonstrating different abilities: list views, cards, animations, forms, payments, etc.
37 |
38 | It uses mocked data:
39 | - Cities available for search
40 | - Hotels
41 | - Rooms
42 | - Reviews
43 | - All mock pictures are generated using [Shedevrum AI](https://shedevrum.ai)
44 |
45 | ## 📖 How to use repo
46 |
47 | Use following instructions
48 |
49 | - 💗 [Get Started](./docs/GetStarted.md) - basic info about Mini Apps development
50 | - 🏠 [Frontend tech guide](./client/README.md) - how to setup Client
51 | - 🎁 [Backend tech guide](./server/README.md) - how to setup Backend
52 | - 🛍️ [Telegram Vue UI Kit](./client/src/presentation/components/README.md) - UI Kit guide
53 | - 💰 [How to setup Payments](./docs/Payments.md) - useful information about Payments integration
54 | - ⛅️ [Deployment guide](./docs/Deployment.md) - how to deploy
55 | - 😎 [Awesome List](./docs/Awesome.md) - list of resources that can be useful when building your own Telegram Mini App
56 |
57 | Feel free to [Open Issue](https://github.com/neSpecc/telebook/issues/new) with your question or suggestion
58 |
--------------------------------------------------------------------------------
/client/.env.example:
--------------------------------------------------------------------------------
1 | # Web client (this) host
2 | VITE_WEB_HOST=https://xxxx-xx-xxx-xxx-xx.ngrok-free.app
3 |
4 | # Backend host
5 | VITE_API_HOST=https://xxxx-xx-xxx-xxx-xx.ngrok-free.app
6 |
--------------------------------------------------------------------------------
/client/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: [
4 | 'standard-with-typescript',
5 | 'plugin:vue/vue3-recommended',
6 | 'plugin:listeners/recommended',
7 | ],
8 | parser: 'vue-eslint-parser',
9 | parserOptions: {
10 | parser: '@typescript-eslint/parser',
11 | tsconfigRootDir: __dirname,
12 | project: [
13 | './tsconfig.json',
14 | './tsconfig.eslint.json',
15 | ],
16 | ecmaVersion: 2022,
17 | extraFileExtensions: ['.vue'],
18 | },
19 | plugins: [
20 | 'clean-timer',
21 | 'listeners',
22 | ],
23 | rules: {
24 | /** Prefer semicolons */
25 | semi: 'off',
26 | '@typescript-eslint/semi': ['error', 'never'],
27 | '@typescript-eslint/member-delimiter-style': [
28 | 'error',
29 | {
30 | multiline: {
31 | delimiter: 'semi',
32 | requireLast: true,
33 | },
34 | singleline: {
35 | delimiter: 'semi',
36 | requireLast: false,
37 | },
38 | multilineDetection: 'brackets',
39 | },
40 | ],
41 |
42 | /** Require trailing comma for better git diffs */
43 | 'comma-dangle': 'off',
44 | '@typescript-eslint/comma-dangle': ['error', 'always-multiline'],
45 |
46 | /** Do not allow spaces before function parenthesis */
47 | 'space-before-function-paren': 'off',
48 | '@typescript-eslint/space-before-function-paren': ['error', {
49 | anonymous: 'always',
50 | named: 'never',
51 | }],
52 |
53 | /** Allow and enforce `... as Foo` type assertion */
54 | '@typescript-eslint/consistent-type-assertions': ['error', {
55 | assertionStyle: 'as',
56 | objectLiteralTypeAssertions: 'allow',
57 | }],
58 |
59 | /**
60 | * Do not enforce nullish coalescing,
61 | * so you can use `valueA || valueB`
62 | */
63 | '@typescript-eslint/prefer-nullish-coalescing': 'off',
64 |
65 | /** Temporarily allow empty interfaces, while in active development */
66 | '@typescript-eslint/no-empty-interface': 'off',
67 |
68 | /**
69 | * Use TS rule to highlight unused variables.
70 | * Default one does not recognize enums
71 | */
72 | 'no-unused-vars': 'off',
73 | '@typescript-eslint/no-unused-vars': 'error',
74 |
75 | /**
76 | * Use TS rule to highlight unnecessary spacings before functions identifiers.
77 | * Default one does not work well with TS interfaces
78 | */
79 | 'func-call-spacing': 'off',
80 | '@typescript-eslint/func-call-spacing': 'error',
81 |
82 | /**
83 | * Disable, as this rule does not play nice with functions, which return promises
84 | */
85 | '@typescript-eslint/no-confusing-void-expression': 'off',
86 |
87 | /**
88 | * Specify an order of top-level tags in vue single-file components
89 | * https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/component-tags-order.md
90 | * https://vuejs.org/v2/style-guide/#Single-file-component-top-level-element-order-recommended
91 | */
92 | 'vue/component-tags-order': ['error', {
93 | order: ['script', 'template', 'style'],
94 | }],
95 |
96 | /** Allow single-word component names */
97 | 'vue/multi-word-component-names': 'off',
98 |
99 | /** Require just one line break after opening and before closing block tags */
100 | 'vue/block-tag-newline': ['error', {
101 | singleline: 'always',
102 | multiline: 'always',
103 | maxEmptyLines: 0,
104 | blocks: {
105 | template: {
106 | singleline: 'always',
107 | multiline: 'always',
108 | maxEmptyLines: 1,
109 | },
110 | },
111 | }],
112 |
113 | 'clean-timer/assign-timer-id': 2,
114 | },
115 | /**
116 | * Rules overrides for .js files
117 | */
118 | overrides: [
119 | {
120 | files: ['*.js', '*.tsx'],
121 | rules: {
122 | '@typescript-eslint/explicit-function-return-type': 'off',
123 | '@typescript-eslint/strict-boolean-expressions': 'off',
124 | '@typescript-eslint/no-floating-promises': 'off',
125 | },
126 | },
127 | ],
128 | }
129 |
--------------------------------------------------------------------------------
/client/README.md:
--------------------------------------------------------------------------------
1 | # Telegram Mini App client
2 |
3 | This project contains an example of Telegram Mini App.
4 |
5 | Telebook — is a fictional hotel booking service integrated to Telegram. It uses completely mocked data of hotels, cities and pictures.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | ## Features and Stack
18 |
19 | - 📦 Vue Telegram UI Kit
20 | - 📲 Declarative screens construction
21 | - ✈️ `useTelegram()` composable that simplifies usage of Telegram Web App SDK
22 | - ❤️🔥 Instant picture previews and on-device cache
23 | - 💎 Clean but simple architecture
24 | - ✨ Hot Reloading
25 | - 💰 Payments support
26 |
27 | ## Telegram Vue UI Kit
28 |
29 | It contains ready-to-use collection of components that could be used to create native-like views that will look perfect on iOS, Android and other devices
30 |
31 | - [Amount](./src/presentation/components/README.md#amount)
32 | - [Avatar](./src/presentation/components/README.md#avatar)
33 | - [DataOverview](./src/presentation/components/README.md#dataoverview)
34 | - [DatePicker](./src/presentation/components/README.md#datepicker)
35 | - [DatePickerCompact ](./src/presentation/components/README.md#datepickercompact)
36 | - [Icon](./src/presentation/components/README.md#icon)
37 | - [Input](./src/presentation/components/README.md#input)
38 | - [List](./src/presentation/components/README.md#list)
39 | - [ListItem](./src/presentation/components/README.md#listitem)
40 | - [ListCard](./src/presentation/components/README.md#listcard)
41 | - [ListItemExpandable](./src/presentation/components/README.md#listitemexpandable)
42 | - [Lottie](./src/presentation/components/README.md#lottie)
43 | - [FixedFooter](./src/presentation/components/README.md#fixedfooter)
44 | - [PageWithHeader](./src/presentation/components/README.md#pagewithheader)
45 | - [Placeholder](./src/presentation/components/README.md#placeholder)
46 | - [Rating](./src/presentation/components/README.md#rating)
47 | - [Section](./src/presentation/components/README.md#section)
48 | - [Sections](./src/presentation/components/README.md#sections)
49 | - [Text](./src/presentation/components/README.md#text)
50 |
51 | Read more in [Telegram Vue UI Kit](./src/presentation/components/README.md) documentation
52 |
53 | ## Get started
54 |
55 | 0. Crate a bot
56 |
57 | Go to [@BotFather](https://t.me/@BotFather), write the `/newbot` command and follow instructions.
58 |
59 | Then, call the `/newapp` comman to create your app. When BotFather will ask about Web App Url, start running the application as described below.
60 |
61 | 1. Install dependencies
62 |
63 | ```
64 | yarn install
65 | ```
66 |
67 | 2. Copy .env.example to .env and fill the variables
68 |
69 | ```
70 | cp .env.example .env
71 | ```
72 |
73 | | Name | Description | Example | Where to get |
74 | | -- | -- | -- | -- |
75 | | VITE_WEB_HOST | Web client endpoint | `https://xxxx-xx-xxx-xxx-xx.ngrok-free.app` | Use ngrok host for local development and your production host on real environemnt |
76 | | VITE_API_HOST | Backend endpoint | `https://xxxx-xx-xxx-xxx-xx.ngrok-free.app` | Use ngrok host for local development and your production host on real environemnt |
77 |
78 | 3. Run
79 |
80 | | Command | Description |
81 | | -- | -- |
82 | | `yarn dev` | Start dev server with Hot Reloading |
83 | | `yarn build` | Compile TS and prepare bundle for production |
84 | | `yarn preview` | Preview production bundle |
85 | | `yarn link` | Check ESLint problems |
86 | | `yarn link:fox` | Autofix ESLint problems when possible |
87 |
88 | 4. When app is running, give a link to @BotFather:
89 |
90 | Copy the URL of your running app and send it to [@BotFather](https://t.me/@BotFather) if he still waiting for it during app creation process. Or call `/myapps`, select our bot and press `Edit Web App URL` button on the Inline Keyboard.
91 |
92 | ### Recommended IDE Setup
93 |
94 | - [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
95 |
96 |
97 | ## Tech Stack and credits
98 |
99 | List of libraries the project relies on
100 |
101 | - TypeScript
102 | - [Vue.js 3](https://vuejs.org) — reactive UI framework
103 | - [Vite](https://vitejs.dev) — build system
104 | - [@twa-dev/sdk](https://github.com/twa-dev/SDK) — Telegram Web App SDK wrapper and Type Definitions
105 | - [@vueuse/core](https://vueuse.org) — Collection of Essential Vue Composition Utilities
106 | - [normalize.css](https://necolas.github.io/normalize.css/) — makes browsers render all elements more consistently
107 | - [Vue Router](https://router.vuejs.org) — helps handling of navigation
108 | - [vue3-lottie](https://vue3-lottie.vercel.app) — Lottie animations player
109 |
110 | ## Directory structure
111 |
112 | The directory structure introduces the simple variation of [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html).
113 | We separate the application into layers, where each layer has its own responsibility. The layers are:
114 |
115 | - `/presentation` - responsible for the UI. Contains all the UI components, screens, and assets
116 | - `/application` - responsible for the presentation-related business logic. It contains the application services, which are actually Vue Composables.
117 | - `/domain` - responsible for the domain logic. It contains the entities and business rules (domain services)
118 | - `/infra` - responsible for transport, store, and utils
119 |
120 | ```
121 | client
122 | ├── application
123 | │ ├── services
124 | │ │ ├── ...
125 | │ │ └── vue composables used by presentation layer
126 | │ └── router.ts - vue-router instance
127 | ├── domain
128 | │ ├── entities
129 | │ │ ├── ...
130 | │ │ └── domain entities (things from the real world)
131 | │ └── services
132 | │ ├── ...
133 | │ └── domain services (business rules)
134 | ├── infra
135 | │ ├── store
136 | │ │ ├── ...
137 | │ │ └── storages used by domain layer
138 | │ ├── transport
139 | │ │ ├── ...
140 | │ │ └── transport layer (telebook api, etc)
141 | │ └── utils
142 | │ ├── ...
143 | │ └── utils used by any layer
144 | └── presentation
145 | ├── assets
146 | │ ├── ...
147 | │ └── icons, fonts, lottie, etc
148 | ├── components
149 | │ ├── ...
150 | │ └── Telegram Vue UI Kit
151 | ├── screens
152 | │ ├── ...
153 | │ └── application screens (pages) used by router
154 | └── styles
155 | ├── ...
156 | └── styles used by presentation layer
157 | ```
158 |
--------------------------------------------------------------------------------
/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Telebook
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "issuegram",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vue-tsc && vite build",
9 | "preview": "vite preview",
10 | "lint": "eslint ./src --ext .ts,.vue",
11 | "lint:fix": "yarn lint --fix"
12 | },
13 | "dependencies": {
14 | "@twa-dev/sdk": "^6.9.0",
15 | "@vueuse/core": "^10.4.1",
16 | "normalize.css": "^8.0.1",
17 | "vue": "^3.3.4",
18 | "vue-router": "4",
19 | "vue3-lottie": "^3.1.0"
20 | },
21 | "devDependencies": {
22 | "@types/node": "^20.7.1",
23 | "@typescript-eslint/eslint-plugin": "^6.7.3",
24 | "@typescript-eslint/parser": "^6.7.3",
25 | "@vitejs/plugin-vue": "^4.2.3",
26 | "eslint": "^8.50.0",
27 | "eslint-config-standard-with-typescript": "^39.1.0",
28 | "eslint-import-resolver-typescript": "^3.6.1",
29 | "eslint-plugin-clean-timer": "^1.0.0",
30 | "eslint-plugin-import": "^2.28.1",
31 | "eslint-plugin-listeners": "^1.2.0",
32 | "eslint-plugin-n": "^16.1.0",
33 | "eslint-plugin-promise": "^6.1.1",
34 | "eslint-plugin-vue": "^9.17.0",
35 | "postcss-apply": "^0.12.0",
36 | "postcss-nested": "^6.0.1",
37 | "postcss-preset-env": "^9.1.4",
38 | "sharp": "^0.32.6",
39 | "typescript": "^5.0.2",
40 | "vite": "^4.4.5",
41 | "vue-tsc": "^1.8.5"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/client/postcss.config.js:
--------------------------------------------------------------------------------
1 | import postcssNested from 'postcss-nested'
2 | import postcssPresetEnv from 'postcss-preset-env'
3 | import postcssApply from 'postcss-apply'
4 |
5 | export default function () {
6 | return {
7 | plugins: [
8 | postcssNested(),
9 | postcssPresetEnv({
10 | features: {
11 | 'nesting-rules': false,
12 | 'custom-properties': {
13 | disableDeprecationNotice: true,
14 | },
15 | },
16 | }),
17 | postcssApply(),
18 | ],
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/client/public/pics/_hotel-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/_hotel-1.jpg
--------------------------------------------------------------------------------
/client/public/pics/_hotel-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/_hotel-2.jpg
--------------------------------------------------------------------------------
/client/public/pics/_room-1-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/_room-1-1.jpg
--------------------------------------------------------------------------------
/client/public/pics/_room-1-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/_room-1-2.jpg
--------------------------------------------------------------------------------
/client/public/pics/hotel-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/hotel-1.jpg
--------------------------------------------------------------------------------
/client/public/pics/hotel-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/hotel-2.jpg
--------------------------------------------------------------------------------
/client/public/pics/hotel-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/hotel-3.jpg
--------------------------------------------------------------------------------
/client/public/pics/hotel-4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/hotel-4.jpg
--------------------------------------------------------------------------------
/client/public/pics/hotel-5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/hotel-5.jpg
--------------------------------------------------------------------------------
/client/public/pics/hotel-6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/hotel-6.jpg
--------------------------------------------------------------------------------
/client/public/pics/hotel-7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/hotel-7.jpg
--------------------------------------------------------------------------------
/client/public/pics/room-1-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/room-1-1.jpg
--------------------------------------------------------------------------------
/client/public/pics/room-1-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/room-1-2.jpg
--------------------------------------------------------------------------------
/client/public/pics/room-2-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/room-2-1.jpg
--------------------------------------------------------------------------------
/client/public/pics/room-2-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/room-2-2.jpg
--------------------------------------------------------------------------------
/client/public/pics/room-2-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/room-2-3.jpg
--------------------------------------------------------------------------------
/client/public/pics/room-3-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/room-3-1.jpg
--------------------------------------------------------------------------------
/client/public/pics/room-3-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/room-3-2.jpg
--------------------------------------------------------------------------------
/client/public/pics/room-3-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/room-3-3.jpg
--------------------------------------------------------------------------------
/client/public/pics/room-4-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/room-4-1.jpg
--------------------------------------------------------------------------------
/client/public/pics/room-4-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/room-4-2.jpg
--------------------------------------------------------------------------------
/client/public/pics/room-4-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/room-4-3.jpg
--------------------------------------------------------------------------------
/client/public/pics/room-5-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/room-5-1.jpg
--------------------------------------------------------------------------------
/client/public/pics/room-5-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/room-5-2.jpg
--------------------------------------------------------------------------------
/client/public/pics/room-6-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/room-6-1.jpg
--------------------------------------------------------------------------------
/client/public/pics/room-6-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/room-6-2.jpg
--------------------------------------------------------------------------------
/client/public/pics/room-6-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/room-6-3.jpg
--------------------------------------------------------------------------------
/client/public/pics/room-7-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/room-7-1.jpg
--------------------------------------------------------------------------------
/client/public/pics/room-7-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neSpecc/telebook/02af111d7c9b128abb958de07a9a68de4d249406/client/public/pics/room-7-2.jpg
--------------------------------------------------------------------------------
/client/public/telebook.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/App.vue:
--------------------------------------------------------------------------------
1 |
27 |
28 |
29 |
30 |
31 |
32 |
36 |
39 |
40 |
41 |
42 |
43 |
44 |
108 |
--------------------------------------------------------------------------------
/client/src/application/router-shims.d.ts:
--------------------------------------------------------------------------------
1 | import 'vue-router'
2 |
3 | /**
4 | * To ensure it is treated as a module, add at least one `export` statement
5 | */
6 | export {}
7 |
8 | declare module 'vue-router' {
9 | interface RouteMeta {
10 | /**
11 | * Place custom route meta props here
12 | */
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/client/src/application/router.ts:
--------------------------------------------------------------------------------
1 | import { type RouteRecordRaw, createRouter, createWebHistory } from 'vue-router'
2 | import Home from '@/presentation/screens/Home.vue'
3 | import Hotel from '@/presentation/screens/Hotel.vue'
4 | import Room from '@/presentation/screens/Room.vue'
5 | import Location from '@/presentation/screens/Location.vue'
6 |
7 | const routes: RouteRecordRaw[] = [
8 | {
9 | path: '/',
10 | component: Home,
11 | },
12 | {
13 | path: '/location',
14 | component: Location,
15 | },
16 | {
17 | path: '/hotel/:id',
18 | component: Hotel,
19 | props: route => ({
20 | id: parseInt(route.params.id as string, 10),
21 | }),
22 | },
23 | {
24 | path: '/room/:hotelId/:roomId',
25 | component: Room,
26 | props: route => ({
27 | hotelId: parseInt(route.params.hotelId as string, 10),
28 | roomId: parseInt(route.params.roomId as string, 10),
29 | }),
30 | },
31 | ]
32 |
33 | const router = createRouter({
34 | history: createWebHistory(),
35 | routes,
36 | })
37 |
38 | export default router
39 |
--------------------------------------------------------------------------------
/client/src/application/services/README.md:
--------------------------------------------------------------------------------
1 | # Application Service
2 |
3 | Local-level logic. Usually, used by Components.
4 |
5 | Could access `Domain` level to perform some business-logic actions
6 |
7 | ## Example of Domain Service logic
8 |
9 | - Retrieve device information
10 | - Working with animations
11 | - Load icons
12 |
--------------------------------------------------------------------------------
/client/src/application/services/index.ts:
--------------------------------------------------------------------------------
1 | import { useScroll } from './useScroll'
2 | import useTelegram from './useTelegram'
3 | import { useLayout } from './useLayout'
4 | import useThumbnail from './useThumbnail'
5 | import useLottie from './useLottie'
6 |
7 | export {
8 | useScroll,
9 | useTelegram,
10 | useLayout,
11 | useThumbnail,
12 | useLottie,
13 | }
14 |
--------------------------------------------------------------------------------
/client/src/application/services/useLayout.ts:
--------------------------------------------------------------------------------
1 | import { createSharedComposable } from '@vueuse/core'
2 | import { type Ref, ref } from 'vue'
3 |
4 | /**
5 | * App layout attributes
6 | */
7 | interface useLayoutComposableState {
8 | /**
9 | * Visible application width
10 | */
11 | appWidth: Ref;
12 | }
13 |
14 | /**
15 | * Service for for working with layout
16 | */
17 | export const useLayout = createSharedComposable((): useLayoutComposableState => {
18 | const appWidth = ref(0)
19 |
20 | if (appWidth.value === 0) {
21 | appWidth.value = document.getElementById('app')?.offsetWidth ?? 0
22 | }
23 |
24 | return {
25 | appWidth,
26 | }
27 | })
28 |
--------------------------------------------------------------------------------
/client/src/application/services/useLottie.ts:
--------------------------------------------------------------------------------
1 | import { onMounted, shallowRef, type ShallowRef } from 'vue'
2 |
3 | interface useLottieComposableState {
4 | /**
5 | * Prepare lottie data
6 | */
7 | animationData: ShallowRef