zrb 0.0.52__py3-none-any.whl → 0.0.53__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. zrb/builtin/generator/fastapp/template/_automate/snake_app_name/cmd/start.sh +1 -1
  2. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/.gitignore +1 -1
  3. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/config.py +14 -5
  4. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/.env.local +4 -0
  5. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/.gitignore +1 -1
  6. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/README.md +0 -0
  7. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/package-lock.json +16 -0
  8. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/package.json +2 -0
  9. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/playwright.config.ts +0 -0
  10. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/postcss.config.js +0 -0
  11. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/app.css +0 -0
  12. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/app.d.ts +0 -0
  13. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/app.html +0 -0
  14. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/index.test.ts +0 -0
  15. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/auth/helper.ts +146 -0
  16. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/auth/store.ts +0 -0
  17. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/auth/type.ts +3 -3
  18. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/components/navigation/Menu.svelte +19 -16
  19. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/components/navigation/Navigation.svelte +14 -11
  20. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/components/navigation/helper.ts +18 -0
  21. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/components/navigation/type.ts +0 -0
  22. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/config/app.ts +9 -0
  23. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/config/navData.ts +6 -17
  24. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/error/helper.ts +12 -0
  25. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/+error.svelte +8 -0
  26. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/+layout.js +0 -0
  27. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/+layout.svelte +4 -3
  28. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/+page.svelte +2 -8
  29. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/static/favicon.png +0 -0
  30. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/static/logo.png +0 -0
  31. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/svelte.config.js +0 -0
  32. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/tailwind.config.js +0 -0
  33. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/tests/test.ts +0 -0
  34. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/tsconfig.json +0 -0
  35. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/vite.config.ts +0 -0
  36. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/component/__init__.py +8 -4
  37. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/component/access_token_scheme.py +14 -0
  38. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/component/access_token_util.py +17 -0
  39. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/component/bearer_token_scheme.py +5 -0
  40. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/component/model/user_model.py +9 -4
  41. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/component/refresh_token_util.py +17 -0
  42. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/core/__init__.py +14 -9
  43. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/core/{token_scheme/oauth2_bearer_token_scheme.py → access_token/scheme.py} +11 -11
  44. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/core/access_token/util.py +69 -0
  45. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/core/authorizer/rpc_authorizer.py +4 -2
  46. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/core/{token_util/jwt_token_util.py → refresh_token/util.py} +18 -6
  47. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/entity/group/api.py +9 -7
  48. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/entity/group/rpc.py +4 -4
  49. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/entity/permission/api.py +9 -7
  50. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/entity/permission/rpc.py +4 -4
  51. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/entity/user/api.py +25 -15
  52. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/entity/user/model.py +44 -19
  53. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/entity/user/rpc.py +19 -11
  54. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/schema/request.py +2 -2
  55. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/schema/token.py +7 -1
  56. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/requirements.txt +1 -0
  57. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/template.env +7 -3
  58. zrb/builtin/generator/fastapp_crud/add.py +7 -21
  59. zrb/builtin/generator/fastapp_crud/add_navigation.py +32 -0
  60. zrb/builtin/generator/fastapp_crud/template/src/kebab-app-name/src/frontend/src/routes/kebab-module-name/kebab-entity-name/+page.svelte +121 -0
  61. zrb/builtin/generator/fastapp_crud/template/src/kebab-app-name/src/frontend/src/routes/kebab-module-name/kebab-entity-name/delete/[id]/+page.svelte +75 -0
  62. zrb/builtin/generator/fastapp_crud/template/src/kebab-app-name/src/frontend/src/routes/kebab-module-name/kebab-entity-name/delete/[id]/+page.ts +5 -0
  63. zrb/builtin/generator/fastapp_crud/template/src/kebab-app-name/src/frontend/src/routes/kebab-module-name/kebab-entity-name/detail/[id]/+page.svelte +54 -0
  64. zrb/builtin/generator/fastapp_crud/template/src/kebab-app-name/src/frontend/src/routes/kebab-module-name/kebab-entity-name/detail/[id]/+page.ts +5 -0
  65. zrb/builtin/generator/fastapp_crud/template/src/kebab-app-name/src/frontend/src/routes/kebab-module-name/kebab-entity-name/new/+page.svelte +52 -0
  66. zrb/builtin/generator/fastapp_crud/template/src/kebab-app-name/src/frontend/src/routes/kebab-module-name/kebab-entity-name/update/[id]/+page.svelte +78 -0
  67. zrb/builtin/generator/fastapp_crud/template/src/kebab-app-name/src/frontend/src/routes/kebab-module-name/kebab-entity-name/update/[id]/+page.ts +5 -0
  68. zrb/builtin/generator/fastapp_crud/template/src/kebab-app-name/src/module/snake_module_name/entity/snake_entity_name/api.py +9 -7
  69. zrb/builtin/generator/fastapp_crud/template/src/kebab-app-name/src/module/snake_module_name/entity/snake_entity_name/rpc.py +4 -4
  70. zrb/builtin/generator/fastapp_field/add.py +203 -4
  71. zrb/builtin/generator/project/template/.gitignore +1 -1
  72. zrb/builtin/generator/simple_python_app/template/src/kebab-app-name/src/.gitignore +1 -1
  73. zrb/helper/util.py +4 -0
  74. zrb/task/resource_maker.py +2 -1
  75. {zrb-0.0.52.dist-info → zrb-0.0.53.dist-info}/METADATA +3 -3
  76. {zrb-0.0.52.dist-info → zrb-0.0.53.dist-info}/RECORD +64 -56
  77. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/auth/auth.ts +0 -83
  78. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/config/config.ts +0 -4
  79. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/cookie/cookie.ts +0 -19
  80. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/about/+page.svelte +0 -2
  81. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/greetings/[slug]/+page.js +0 -6
  82. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/greetings/[slug]/+page.svelte +0 -5
  83. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/sample/+page.svelte +0 -37
  84. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/sample/delete/[id]/+page.svelte +0 -1
  85. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/component/token_scheme.py +0 -11
  86. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/component/token_util.py +0 -17
  87. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/core/token_scheme/token_sheme.py +0 -5
  88. zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/core/token_util/token_util.py +0 -13
  89. /zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/core/{token_scheme/__init__.py → access_token/_init_.py} +0 -0
  90. /zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/core/{token_util/__init__.py → refresh_token/_init_.py} +0 -0
  91. {zrb-0.0.52.dist-info → zrb-0.0.53.dist-info}/LICENSE +0 -0
  92. {zrb-0.0.52.dist-info → zrb-0.0.53.dist-info}/WHEEL +0 -0
  93. {zrb-0.0.52.dist-info → zrb-0.0.53.dist-info}/entry_points.txt +0 -0
@@ -1,11 +1,11 @@
1
- from typing import Any, Mapping
1
+ from typing import Any, Mapping, List, Union
2
2
  from logging import Logger
3
3
  from core.messagebus import Publisher
4
4
  from core.rpc import Caller, Server
5
5
  from core.repo import SearchFilter
6
6
  from module.auth.component.model.user_model import user_model
7
7
  from module.auth.schema.user import UserData, UserLogin
8
- from module.auth.schema.token import TokenData
8
+ from module.auth.schema.token import AccessTokenData
9
9
 
10
10
 
11
11
  def register_rpc(
@@ -32,20 +32,28 @@ def register_rpc(
32
32
 
33
33
  @rpc_server.register('auth_is_user_authorized')
34
34
  async def is_user_having_permission(
35
- id: str, *permission_names: str
35
+ id: str, permission_name: Union[str, List[str]]
36
36
  ) -> Mapping[str, bool]:
37
37
  '''
38
38
  Used by RPC Authenticator
39
39
  '''
40
- return await user_model.is_authorized(id, *permission_names)
40
+ if isinstance(permission_name, str):
41
+ return await user_model.is_authorized(id, permission_name)
42
+ return await user_model.is_authorized(id, *permission_name)
41
43
 
42
44
  @rpc_server.register('auth_create_token')
43
- async def create_token(login_data: Mapping[str, str]) -> str:
44
- return await user_model.create_token(UserLogin(**login_data))
45
+ async def create_token(login_data: Mapping[str, str]) -> Mapping[str, str]:
46
+ result = await user_model.create_auth_token(UserLogin(**login_data))
47
+ return result.dict()
45
48
 
46
49
  @rpc_server.register('auth_refresh_token')
47
- async def refresh_token(token: str) -> str:
48
- return await user_model.refresh_token(token)
50
+ async def refresh_token(
51
+ refresh_token: str, access_token: str
52
+ ) -> Mapping[str, str]:
53
+ result = await user_model.refresh_auth_token(
54
+ refresh_token, access_token
55
+ )
56
+ return result.dict()
49
57
 
50
58
  @rpc_server.register('auth_get_user')
51
59
  async def get(
@@ -77,7 +85,7 @@ def register_rpc(
77
85
  data: Mapping[str, Any],
78
86
  user_token_data: Mapping[str, Any]
79
87
  ) -> Mapping[str, Any]:
80
- user_token_data = TokenData(**user_token_data)
88
+ user_token_data = AccessTokenData(**user_token_data)
81
89
  data['created_by'] = user_token_data.user_id
82
90
  data['updated_by'] = user_token_data.user_id
83
91
  row = await user_model.insert(
@@ -91,7 +99,7 @@ def register_rpc(
91
99
  data: Mapping[str, Any],
92
100
  user_token_data: Mapping[str, Any]
93
101
  ) -> Mapping[str, Any]:
94
- user_token_data = TokenData(**user_token_data)
102
+ user_token_data = AccessTokenData(**user_token_data)
95
103
  data['updated_by'] = user_token_data.user_id
96
104
  row = await user_model.update(
97
105
  id=id, data=UserData(**data)
@@ -103,6 +111,6 @@ def register_rpc(
103
111
  id: str,
104
112
  user_token_data: Mapping[str, Any]
105
113
  ) -> Mapping[str, Any]:
106
- user_token_data = TokenData(**user_token_data)
114
+ user_token_data = AccessTokenData(**user_token_data)
107
115
  row = await user_model.delete(id=id)
108
116
  return row.dict()
@@ -3,8 +3,8 @@ from pydantic import BaseModel
3
3
 
4
4
 
5
5
  class RefreshTokenRequest(BaseModel):
6
- token: str
6
+ access_token: str
7
7
 
8
8
 
9
9
  class IsAuthorizedRequest(BaseModel):
10
- permission_names: List[str]
10
+ permission_names: List[str]
@@ -1,12 +1,18 @@
1
1
  from pydantic import BaseModel
2
2
 
3
3
 
4
- class TokenData(BaseModel):
4
+ class AccessTokenData(BaseModel):
5
5
  user_id: str
6
6
  username: str
7
7
  expire_seconds: int
8
8
 
9
9
 
10
+ class RefreshTokenData(BaseModel):
11
+ user_id: str
12
+ expire_seconds: int
13
+
14
+
10
15
  class TokenResponse(BaseModel):
11
16
  access_token: str
17
+ refresh_token: str
12
18
  token_type: str
@@ -9,6 +9,7 @@ pydantic==1.10.6
9
9
  psycopg2-binary==2.9.6
10
10
  python-jose==3.3.0
11
11
  uvicorn==0.20.0
12
+ watchfiles==0.19.0
12
13
  python-multipart==0.0.6
13
14
 
14
15
  # for testing
@@ -7,10 +7,14 @@ APP_MAX_NOT_READY=10
7
7
 
8
8
  PUBLIC_BRAND=PascalAppName
9
9
  PUBLIC_TITLE=PascalAppName
10
- PUBLIC_AUTH_TOKEN_COOKIE_KEY=auth_token
10
+ PUBLIC_AUTH_ACCESS_TOKEN_COOKIE_KEY=access_token
11
+ PUBLIC_AUTH_REFRESH_TOKEN_COOKIE_KEY=refresh_token
12
+
13
+ APP_AUTH_ACCESS_TOKEN_EXPIRE_SECONDS=300
14
+ APP_AUTH_ACCESS_TOKEN_TYPE=jwt
15
+ APP_AUTH_REFRESH_TOKEN_EXPIRE_SECONDS=21600
16
+ APP_AUTH_REFRESH_TOKEN_TYPE=jwt
11
17
 
12
- APP_AUTH_TOKEN_EXPIRE_SECONDS=300
13
- APP_AUTH_TOKEN_TYPE=jwt
14
18
  APP_AUTH_JWT_TOKEN_SECRET_KEY=Alch3mist
15
19
  APP_AUTH_JWT_TOKEN_ALGORITHM=HS256
16
20
 
@@ -5,15 +5,16 @@ from ....task.decorator import python_task
5
5
  from ....task.cmd_task import CmdTask
6
6
  from ....task.resource_maker import ResourceMaker
7
7
  from ....runner import runner
8
+ from ....helper import util
9
+ from ....helper.codemod.add_import_module import add_import_module
10
+ from ....helper.codemod.append_code_to_function import append_code_to_function
11
+ from ....helper.file.text import read_text_file_async, write_text_file_async
8
12
  from .._common.input import (
9
13
  project_dir_input, app_name_input, module_name_input, entity_name_input,
10
14
  plural_entity_name_input, main_column_name_input
11
15
  )
12
16
  from .._common.helper import validate_project_dir
13
- from ....helper import util
14
- from ....helper.codemod.add_import_module import add_import_module
15
- from ....helper.codemod.append_code_to_function import append_code_to_function
16
- from ....helper.file.text import read_text_file_async, write_text_file_async
17
+ from .add_navigation import create_add_navigation_task
17
18
 
18
19
  import asyncio
19
20
  import os
@@ -128,26 +129,11 @@ prepare_codemod = CmdTask(
128
129
  ]
129
130
  )
130
131
 
131
-
132
- nav_file_path = '{{input.project_dir}}/src/{{util.to_kebab_case(input.app_name)}}/src/frontend/src/lib/config/navData.ts' # noqa
133
- var_name = 'navData'
134
- title = '{{util.to_pascal_case(input.entity_name)}}'
135
- url = '{{util.to_kebab_case(input.module_name)}}/{{util.to_kebab_case(input.entity_name)}}' # noqa
136
- permission = ''
137
- add_navigation = CmdTask(
138
- name='add-navigation',
139
- inputs=[
140
- project_dir_input,
141
- app_name_input,
142
- module_name_input,
143
- entity_name_input,
144
- ],
132
+ add_navigation = create_add_navigation_task(
145
133
  upstreams=[
146
134
  copy_resource,
147
135
  prepare_codemod,
148
- ],
149
- retry=0,
150
- cmd=f'node {codemod_dir}/dist/addNav.js "{nav_file_path}" "{var_name}" "{title}" "{url}" "{permission}"' # noqa
136
+ ]
151
137
  )
152
138
 
153
139
 
@@ -0,0 +1,32 @@
1
+ from typing import List
2
+ from ....task.task import Task
3
+ from ....task.cmd_task import CmdTask
4
+ from .._common.input import (
5
+ project_dir_input, app_name_input, module_name_input, entity_name_input,
6
+ )
7
+
8
+ import os
9
+
10
+ current_dir = os.path.dirname(__file__)
11
+ codemod_dir = os.path.join(current_dir, 'nodejs', 'codemod')
12
+
13
+ nav_config_file_path = '{{input.project_dir}}/src/{{util.to_kebab_case(input.app_name)}}/src/frontend/src/lib/config/navData.ts' # noqa
14
+ nav_var_name = 'navData'
15
+ nav_title = '{{util.to_pascal_case(input.entity_name)}}'
16
+ nav_url = '/{{util.to_kebab_case(input.module_name)}}/{{util.to_kebab_case(input.entity_name)}}' # noqa
17
+ nav_permission = '{{util.to_snake_case(input.module_name)}}:{{util.to_snake_case(input.entity_name)}}:get' # noqa
18
+
19
+
20
+ def create_add_navigation_task(upstreams: List[Task]) -> Task:
21
+ return CmdTask(
22
+ name='add-navigation',
23
+ inputs=[
24
+ project_dir_input,
25
+ app_name_input,
26
+ module_name_input,
27
+ entity_name_input,
28
+ ],
29
+ upstreams=upstreams,
30
+ retry=0,
31
+ cmd=f'node {codemod_dir}/dist/addNav.js "{nav_config_file_path}" "{nav_var_name}" "{nav_title}" "{nav_url}" "{nav_permission}"' # noqa
32
+ )
@@ -0,0 +1,121 @@
1
+ <script lang="ts">
2
+ import axios from 'axios';
3
+ import { onMount } from 'svelte';
4
+ import { ensureAccessToken } from '$lib/auth/helper';
5
+ import { getErrorMessage } from '$lib/error/helper';
6
+
7
+ let limit: number = 5;
8
+ let pageIndex: number = 0;
9
+ let count: number = 0;
10
+ let keyword: string = '';
11
+ let isAlertVisible: boolean = false;
12
+ let errorMessage: string = '';
13
+
14
+ let limitOptions: Array<number> = [5, 10, 30, 50, 100];
15
+ let pageIndexes: Array<number> = [];
16
+ let rows: Array<any> = [];
17
+
18
+ onMount(async() => {
19
+ await loadRows();
20
+ });
21
+
22
+ async function loadRows() {
23
+ const accessToken = await ensureAccessToken();
24
+ try {
25
+ const encodedKeyword = encodeURIComponent(keyword);
26
+ const offset = limit * pageIndex
27
+ const response = await axios.get(
28
+ `/api/v1/kebab-module-name/kebab-plural-entity-name?limit=${limit}&offset=${offset}&keyword=${encodedKeyword}`,
29
+ {headers: {Authorization: `Bearer ${accessToken}`}}
30
+ );
31
+ if (response?.status == 200 && response?.data) {
32
+ count = response.data.count;
33
+ rows = response.data.data;
34
+ pageIndexes = [];
35
+ for (let i = 0; i*limit < count; i++) {
36
+ pageIndexes.push(i);
37
+ }
38
+ return;
39
+ }
40
+ errorMessage = 'Unknown error';
41
+ } catch(error) {
42
+ console.error(error);
43
+ errorMessage = getErrorMessage(error);
44
+ }
45
+ isAlertVisible = true;
46
+ }
47
+
48
+ async function onPaginationClick(index: number) {
49
+ pageIndex = index;
50
+ await loadRows();
51
+ }
52
+ </script>
53
+
54
+ <h1 class="text-3xl">Book</h1>
55
+ <div class="overflow-x-auto">
56
+
57
+ <div class="flex items-center mb-5 mt-5">
58
+ <label for="limit" class="mr-2">Row per page:</label>
59
+ <select id="limit" class="select select-bordered mr-2" bind:value={limit} on:change={loadRows}>
60
+ {#each limitOptions as limitOption}
61
+ {#if limitOption == limit }
62
+ <option selected>{limitOption}</option>
63
+ {:else}
64
+ <option>{limitOption}</option>
65
+ {/if}
66
+ {/each}
67
+ </select>
68
+ <input type="text" placeholder="Search..." class="input input-bordered mr-2" bind:value={keyword} />
69
+ <button class="btn btn-primary btn-square" on:click={loadRows}>
70
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
71
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
72
+ </svg>
73
+ </button>
74
+ <a class="btn ml-5" href="./new">New</a>
75
+ </div>
76
+
77
+ <div class="alert alert-error shadow-lg mt-5 mb-5 {isAlertVisible? 'visible': 'hidden'}">
78
+ <div>
79
+ <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
80
+ <span>{errorMessage}</span>
81
+ </div>
82
+ </div>
83
+
84
+ <table class="table w-full">
85
+ <!-- head -->
86
+ <thead>
87
+ <tr>
88
+ <th></th>
89
+ <th>Human readable column name</th>
90
+ <!-- DON'T DELETE: insert new column header here-->
91
+ <th></th>
92
+ </tr>
93
+ </thead>
94
+ <tbody>
95
+ {#each rows as row}
96
+ <tr>
97
+ <th>{row.id}</th>
98
+ <td>{row.snake_column_name}</td>
99
+ <!-- DON'T DELETE: insert new column here-->
100
+ <td>
101
+ <a class="btn" href="./detail/{row.id}">Detail</a>
102
+ <a class="btn" href="./update/{row.id}">Update</a>
103
+ <a class="btn btn-accent" href="./delete/{row.id}">Delete</a>
104
+ </td>
105
+ </tr>
106
+ {/each}
107
+ </tbody>
108
+ </table>
109
+
110
+ <div class="btn-group justify-center w-full">
111
+ {#each pageIndexes as pageIndexOption}
112
+ {#if pageIndexOption == pageIndex}
113
+ <button class="btn btn-disabled">{pageIndexOption + 1}</button>
114
+ {:else}
115
+ <button class="btn" on:click={async () => await onPaginationClick(pageIndexOption)}>
116
+ {pageIndexOption + 1}
117
+ </button>
118
+ {/if}
119
+ {/each}
120
+ </div>
121
+ </div>
@@ -0,0 +1,75 @@
1
+ <script lang="ts">
2
+ import axios from 'axios';
3
+ import { onMount } from 'svelte';
4
+ import { goto } from '$app/navigation';
5
+ import { ensureAccessToken } from '$lib/auth/helper';
6
+ import { getErrorMessage } from '$lib/error/helper';
7
+
8
+ export let data: {id?: string} = {};
9
+
10
+ let row: any = {};
11
+ let isAlertVisible: boolean = false;
12
+ let errorMessage: string = '';
13
+
14
+ onMount(async() => {
15
+ await loadRow();
16
+ });
17
+
18
+ async function loadRow() {
19
+ const accessToken = await ensureAccessToken();
20
+ try {
21
+ const response = await axios.get(
22
+ `/api/v1/kebab-module-name/kebab-plural-entity-name/${data.id}`,
23
+ {headers: {Authorization: `Bearer ${accessToken}`}}
24
+ );
25
+ if (response?.status == 200 && response?.data) {
26
+ row = response.data;
27
+ return;
28
+ }
29
+ errorMessage = 'Unknown error';
30
+ } catch(error) {
31
+ console.error(error);
32
+ errorMessage = getErrorMessage(error);
33
+ }
34
+ isAlertVisible = true;
35
+ }
36
+
37
+ async function onDeleteClick() {
38
+ const accessToken = await ensureAccessToken();
39
+ try {
40
+ const response = await axios.delete(
41
+ `/api/v1/kebab-module-name/kebab-plural-entity-name/${data.id}`,
42
+ {headers: {Authorization: `Bearer ${accessToken}`}}
43
+ );
44
+ if (response?.status == 200 && response?.data) {
45
+ await goto('../../');
46
+ return;
47
+ }
48
+ errorMessage = 'Unknown error';
49
+ } catch(error) {
50
+ console.error(error);
51
+ errorMessage = getErrorMessage(error);
52
+ }
53
+ isAlertVisible = true;
54
+ }
55
+ </script>
56
+ <h1 class="text-3xl">Book</h1>
57
+
58
+ <form class="max-w-md mx-auto bg-gray-100 p-6 rounded-md mt-5 mb-5">
59
+ <h2 class="text-xl font-bold mb-4">Delete Book {data.id}</h2>
60
+ <div class="mb-4">
61
+ <label class="block text-gray-700 font-bold mb-2" for="kebab-column-name">Human readable column name</label>
62
+ <span id="kebab-column-name">{row.snake_column_name}</span>
63
+ </div>
64
+ <!-- DON'T DELETE: insert new field here-->
65
+ <a href="#top" class="btn btn-accent" on:click={onDeleteClick}>Delete</a>
66
+ <a href="../../" class="btn">Cancel</a>
67
+
68
+ <div class="alert alert-error shadow-lg mt-5 {isAlertVisible? 'visible': 'hidden'}">
69
+ <div>
70
+ <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
71
+ <span>{errorMessage}</span>
72
+ </div>
73
+ </div>
74
+
75
+ </form>
@@ -0,0 +1,5 @@
1
+ export async function load({ params }) {
2
+ return {
3
+ id: params?.id
4
+ };
5
+ }
@@ -0,0 +1,54 @@
1
+ <script lang="ts">
2
+ import axios from 'axios';
3
+ import { onMount } from 'svelte';
4
+ import { ensureAccessToken } from '$lib/auth/helper';
5
+ import { getErrorMessage } from '$lib/error/helper';
6
+
7
+ export let data: {id?: string} = {};
8
+
9
+ let row: any = {};
10
+ let isAlertVisible: boolean = false;
11
+ let errorMessage: string = '';
12
+
13
+ onMount(async() => {
14
+ await loadRow();
15
+ });
16
+
17
+ async function loadRow() {
18
+ const accessToken = await ensureAccessToken();
19
+ try {
20
+ const response = await axios.get(
21
+ `/api/v1/kebab-module-name/kebab-plural-entity-name/${data.id}`,
22
+ {headers: {Authorization: `Bearer ${accessToken}`}}
23
+ );
24
+ if (response?.status == 200 && response?.data) {
25
+ row = response.data;
26
+ return;
27
+ }
28
+ errorMessage = 'Unknown error';
29
+ } catch(error) {
30
+ console.error(error);
31
+ errorMessage = getErrorMessage(error);
32
+ }
33
+ isAlertVisible = true;
34
+ }
35
+ </script>
36
+ <h1 class="text-3xl">Book</h1>
37
+
38
+ <form class="max-w-md mx-auto bg-gray-100 p-6 rounded-md mt-5 mb-5">
39
+ <h2 class="text-xl font-bold mb-4">Show Book {data.id}</h2>
40
+ <div class="mb-4">
41
+ <label class="block text-gray-700 font-bold mb-2" for="kebab-column-name">Human readable column name</label>
42
+ <span id="kebab-column-name">{row.snake_column_name}</span>
43
+ </div>
44
+ <!-- DON'T DELETE: insert new field here-->
45
+ <a href="../../" class="btn btn-primary">Show others</a>
46
+
47
+ <div class="alert alert-error shadow-lg mt-5 {isAlertVisible? 'visible': 'hidden'}">
48
+ <div>
49
+ <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
50
+ <span>{errorMessage}</span>
51
+ </div>
52
+ </div>
53
+
54
+ </form>
@@ -0,0 +1,5 @@
1
+ export async function load({ params }) {
2
+ return {
3
+ id: params?.id
4
+ };
5
+ }
@@ -0,0 +1,52 @@
1
+ <script lang="ts">
2
+ import axios from 'axios';
3
+ import { goto } from '$app/navigation';
4
+ import { ensureAccessToken } from '$lib/auth/helper';
5
+ import { getErrorMessage } from '$lib/error/helper';
6
+
7
+ let row: any = {}
8
+ let isAlertVisible: boolean = false;
9
+ let isSaving: boolean = false;
10
+ let errorMessage: string = '';
11
+
12
+ async function onSaveClick() {
13
+ isSaving = true
14
+ const accessToken = await ensureAccessToken();
15
+ try {
16
+ const response = await axios.post(
17
+ '/api/v1/kebab-module-name/kebab-plural-entity-name', row, {headers: {Authorization: `Bearer ${accessToken}`}}
18
+ );
19
+ if (response?.status == 200) {
20
+ await goto('../');
21
+ return;
22
+ }
23
+ errorMessage = 'Unknown error';
24
+ } catch(error) {
25
+ console.error(error);
26
+ errorMessage = getErrorMessage(error);
27
+ }
28
+ isAlertVisible = true;
29
+ isSaving = false;
30
+ }
31
+ </script>
32
+
33
+ <h1 class="text-3xl">Book</h1>
34
+
35
+ <form class="max-w-md mx-auto bg-gray-100 p-6 rounded-md mt-5 mb-5">
36
+ <h2 class="text-xl font-bold mb-4">New Book</h2>
37
+ <div class="mb-4">
38
+ <label class="block text-gray-700 font-bold mb-2" for="kebab-column-name">Human readable column name</label>
39
+ <input type="text" class="input w-full" id="kebab-column-name" placeholder="Human readable column name" bind:value={row.snake_column_name}>
40
+ </div>
41
+ <!-- DON'T DELETE: insert new field here-->
42
+ <a href="#top" class="btn btn-primary {isSaving ? 'btn-disabled': '' }" on:click={onSaveClick}>Save</a>
43
+ <a href="../" class="btn">Cancel</a>
44
+
45
+ <div class="alert alert-error shadow-lg mt-5 {isAlertVisible? 'visible': 'hidden'}">
46
+ <div>
47
+ <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
48
+ <span>{errorMessage}</span>
49
+ </div>
50
+ </div>
51
+
52
+ </form>
@@ -0,0 +1,78 @@
1
+ <script lang="ts">
2
+ import axios from 'axios';
3
+ import { onMount } from 'svelte';
4
+ import { goto } from '$app/navigation';
5
+ import { ensureAccessToken } from '$lib/auth/helper';
6
+ import { getErrorMessage } from '$lib/error/helper';
7
+
8
+ export let data: {id?: string} = {};
9
+
10
+ let row: any = {}
11
+ let isAlertVisible: boolean = false;
12
+ let isSaving: boolean = false;
13
+ let errorMessage: string = '';
14
+
15
+ onMount(async() => {
16
+ await loadRow();
17
+ });
18
+
19
+ async function loadRow() {
20
+ const accessToken = await ensureAccessToken();
21
+ try {
22
+ const response = await axios.get(
23
+ `/api/v1/kebab-module-name/kebab-plural-entity-name/${data.id}`,
24
+ {headers: {Authorization: `Bearer ${accessToken}`}}
25
+ );
26
+ if (response?.status == 200 && response?.data) {
27
+ row = response.data;
28
+ return;
29
+ }
30
+ errorMessage = 'Unknown error';
31
+ } catch(error) {
32
+ console.error(error);
33
+ errorMessage = getErrorMessage(error);
34
+ }
35
+ isAlertVisible = true;
36
+ }
37
+
38
+ async function onSaveClick() {
39
+ isSaving = true
40
+ const accessToken = await ensureAccessToken();
41
+ try {
42
+ const response = await axios.put(
43
+ `/api/v1/kebab-module-name/kebab-plural-entity-name/${data.id}`, row, {headers: {Authorization: `Bearer ${accessToken}`}}
44
+ );
45
+ if (response?.status == 200) {
46
+ await goto('../../');
47
+ return;
48
+ }
49
+ errorMessage = 'Unknown error';
50
+ } catch(error) {
51
+ console.error(error);
52
+ errorMessage = getErrorMessage(error);
53
+ }
54
+ isAlertVisible = true;
55
+ isSaving = false;
56
+ }
57
+ </script>
58
+
59
+ <h1 class="text-3xl">Book</h1>
60
+
61
+ <form class="max-w-md mx-auto bg-gray-100 p-6 rounded-md mt-5 mb-5">
62
+ <h2 class="text-xl font-bold mb-4">Update Book</h2>
63
+ <div class="mb-4">
64
+ <label class="block text-gray-700 font-bold mb-2" for="kebab-column-name">Human readable column name</label>
65
+ <input type="text" class="input w-full" id="kebab-column-name" placeholder="Human readable column name" bind:value={row.snake_column_name}>
66
+ </div>
67
+ <!-- DON'T DELETE: insert new field here-->
68
+ <a href="#top" class="btn btn-primary {isSaving ? 'btn-disabled': '' }" on:click={onSaveClick}>Save</a>
69
+ <a href="../../" class="btn">Cancel</a>
70
+
71
+ <div class="alert alert-error shadow-lg mt-5 {isAlertVisible? 'visible': 'hidden'}">
72
+ <div>
73
+ <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
74
+ <span>{errorMessage}</span>
75
+ </div>
76
+ </div>
77
+
78
+ </form>
@@ -0,0 +1,5 @@
1
+ export async function load({ params }) {
2
+ return {
3
+ id: params?.id
4
+ };
5
+ }