changed
CHANGELOG.md
|
@@ -0,0 +1,311 @@
|
1
|
+ # Changelog
|
2
|
+ All notable changes to this project will be documented in this file.
|
3
|
+
|
4
|
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
5
|
+ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
6
|
+
|
7
|
+ ## [v0.4.0]
|
8
|
+ ### Other
|
9
|
+ - feat: :sparkles: add versioce for version bumping
|
10
|
+ - Merge pull request #62 from curiosum-dev/feature/ui-translation-improvements
|
11
|
+
|
12
|
+ Improve UX when editing many translations
|
13
|
+ - Fix
|
14
|
+ - Merge branch develop into feature/ui-translation-improvements
|
15
|
+ - Merge pull request #59 from curiosum-dev/feature/add_support_for_multiple_apps
|
16
|
+
|
17
|
+ Add support for multiple apps
|
18
|
+ - Add missing @moduledoc
|
19
|
+ - Merge branch develop into feature/add_support_for_multiple_apps
|
20
|
+ - Merge pull request #61 from curiosum-dev/feature/support-nested-scopes
|
21
|
+
|
22
|
+ Add support for nested scopes and different main path
|
23
|
+ - Merge branch develop into feature/support-nested-scopes
|
24
|
+ - Trigger workflow
|
25
|
+ - Merge pull request #60 from curiosum-dev/fix/multiple-locales-for-messages-fixes
|
26
|
+
|
27
|
+ Fix translation preloads to preload only necessary data
|
28
|
+ - Merge branch develop into fix/multiple-locales-for-messages-fixes
|
29
|
+ - Trigger workflow
|
30
|
+ - Merge pull request #56 from curiosum-dev/fix/multiline-translations
|
31
|
+
|
32
|
+ Add support for multi-line msgids
|
33
|
+ - Merge branch develop into fix/multiline-translations
|
34
|
+ - Trigger workflow
|
35
|
+ - Merge pull request #52 from curiosum-dev/fix/parse_params_to_avoid_500s
|
36
|
+
|
37
|
+ Parse params and add support for different ID types
|
38
|
+ - Refactor translations_live.ex
|
39
|
+ - Trigger workflow
|
40
|
+ - Extract logic of `parse_filters/1`s reduce to `parse_filter/2`
|
41
|
+ - Return error when `id_parse_function` provided with MFA with invalid arity
|
42
|
+ - Add support for different ID types
|
43
|
+ - Parse params and and redirects
|
44
|
+ - Merge pull request #80 from curiosum-dev/feature/improve-ci
|
45
|
+
|
46
|
+ Feature/improve ci
|
47
|
+ - Bump credo and dialyxir versions
|
48
|
+ - Suppress warning about router.ex
|
49
|
+ - Bump uri_query version
|
50
|
+ - Bump ecto versions
|
51
|
+ - Use Ubuntu 24.04 LTS
|
52
|
+ - Set env
|
53
|
+ - Remove unsupported OTP version
|
54
|
+ - Add supported Elixir versions
|
55
|
+ - Fix naming across file
|
56
|
+ - Fix naming
|
57
|
+ - Improve CI
|
58
|
+ - Merge pull request #77 from curiosum-dev/feat/remove-devenv
|
59
|
+
|
60
|
+ ❄️ Remove devenv
|
61
|
+ - Merge pull request #79 from curiosum-dev/fix/align-with-main
|
62
|
+
|
63
|
+ Fix/align with main
|
64
|
+ - Remove autogenerated dummy test
|
65
|
+ - Fix credo
|
66
|
+ - ❄️ Remove devenv
|
67
|
+ - Add chevrons to `Icons` module
|
68
|
+ - Add `Colors` module
|
69
|
+ - Improve readability of `Router`
|
70
|
+ - Add `dashboard_path` helper to verified routes
|
71
|
+ - Restore params after saving a translation
|
72
|
+ - Add a way to clear all filters at once
|
73
|
+ - Fixes to migrations
|
74
|
+ - Fix to `application_source_form_live`
|
75
|
+ - Add a way to create new application source
|
76
|
+ - Add support for nested scopes and different main path
|
77
|
+ - Fix translation preloads to preload only necessary data
|
78
|
+ - Changes to make import export plugin work
|
79
|
+ - Replace `parse_msgid/1` with just `Enum.join`
|
80
|
+ - Add support for multiline msgids
|
81
|
+ - Add application source
|
82
|
+ - Add support for SQLite3 (#51)
|
83
|
+
|
84
|
+ * Add support for SQLite3
|
85
|
+
|
86
|
+ * Fix current version
|
87
|
+ - Recover filters from params on mount in the translations list (#49)
|
88
|
+
|
89
|
+ * Improve UX when using filters
|
90
|
+
|
91
|
+ * Properly find index in Select
|
92
|
+ - 🐛 Fix pagination failing to parse int (#44)
|
93
|
+ - Support translations during compilation (#48)
|
94
|
+
|
95
|
+ * Support translations during compilation
|
96
|
+
|
97
|
+ * Remove Logger
|
98
|
+
|
99
|
+ * Fix credo issues
|
100
|
+
|
101
|
+ * Add doc to `compiling?` function
|
102
|
+ - Support for newer phoenix_html 4 with phoenix_html_helpers because newer version has a little different api (#47)
|
103
|
+
|
104
|
+ * html_helpers
|
105
|
+
|
106
|
+ Signed-off-by: Jan Jakůbek <[email protected]>
|
107
|
+
|
108
|
+ * delete_use_PhoenixHtml
|
109
|
+
|
110
|
+ Signed-off-by: Jan Jakůbek <[email protected]>
|
111
|
+
|
112
|
+ ---------
|
113
|
+
|
114
|
+ Signed-off-by: Jan Jakůbek <[email protected]>
|
115
|
+ - Merge pull request #42 from curiosum-dev/feature/child-lv-components
|
116
|
+
|
117
|
+ Dashboard allow child live view components
|
118
|
+ - 📝 Add docs to DashboardLive
|
119
|
+ - 💫 Support child LV dashboard components
|
120
|
+ - Merge pull request #41 from curiosum-dev/feature/stricter-po-file-search
|
121
|
+
|
122
|
+ 🚦 Search for PO files only in priv/gettext
|
123
|
+ - Merge pull request #40 from oliver-kriska/feature/multi-line-msgstr
|
124
|
+
|
125
|
+ PO Extractor - allow to import multi-lines msgstr
|
126
|
+ - 🚦 Search for PO files only in priv/gettext
|
127
|
+ - Update README.md
|
128
|
+ - PO Extractor - allow to import multi-lines msgstr
|
129
|
+ - Merge pull request #38 from curiosum-dev/chore/kanta-sync-readme-plugins-list-update
|
130
|
+
|
131
|
+ Update README.md
|
132
|
+ - Update README.md
|
133
|
+
|
134
|
+ Adds Kanta sync to the plugins list in README Table of contents
|
135
|
+
|
136
|
+ ## [0.3.1]
|
137
|
+ ### Other
|
138
|
+ - Merge pull request #37 from curiosum-dev/release/0.3.1
|
139
|
+
|
140
|
+ Release/0.3.1
|
141
|
+ - Merge branch main into release/0.3.1
|
142
|
+ - Merge pull request #36 from curiosum-dev/feature/api-endpoint
|
143
|
+
|
144
|
+ Feature/api endpoint
|
145
|
+ - Add optional API authorization, resolve minor naming issues
|
146
|
+ - Add @moduledoc to APIAuthPlug
|
147
|
+ - Add API endpoints
|
148
|
+ - Merge branch develop into feature/api-endpoint
|
149
|
+ - Create API scaffolding
|
150
|
+
|
151
|
+ ## [0.3.0]
|
152
|
+ ### Other
|
153
|
+ - Merge pull request #35 from curiosum-dev/release/0.3.0
|
154
|
+
|
155
|
+ Release/0.3.0
|
156
|
+ - Update phoenix_live_view to 0.20
|
157
|
+ - Update Kanta version to 0.3.0
|
158
|
+ - Merge pull request #34 from curiosum-dev/fix/js-error-on-liveview-page-change
|
159
|
+
|
160
|
+ Fix JS error on LiveView page change
|
161
|
+ - Get rid of deprecated live_component/2
|
162
|
+ - Fix JS error on LiveView page change
|
163
|
+ - Merge pull request #32 from FranciscoLira/fix/router-import-missing
|
164
|
+
|
165
|
+ Fix/router import missing
|
166
|
+ - Merge pull request #21 from curiosum-dev/feature/improved-translations-search
|
167
|
+
|
168
|
+ Improve translations search
|
169
|
+ - Require opts keyword list in join_resource/3
|
170
|
+ - Improve efficiency of messages list query
|
171
|
+ - Merge branch develop into feature/improved-translations-search
|
172
|
+ - fix:README missing import in router
|
173
|
+ - Improve translations search
|
174
|
+
|
175
|
+ ## [0.2.2]
|
176
|
+ ### Other
|
177
|
+ - Merge pull request #30 from curiosum-dev/release/0.2.2
|
178
|
+
|
179
|
+ Release/0.2.2
|
180
|
+ - Merge branch main into release/0.2.2
|
181
|
+ - Merge pull request #29 from curiosum-dev/chore/update-powriter-information-in-readme
|
182
|
+
|
183
|
+ Update information about POWriter
|
184
|
+ - Update information about POWriter
|
185
|
+ - Merge pull request #27 from curiosum-dev/feature/readme-and-sidebar-badges
|
186
|
+
|
187
|
+ Add badges
|
188
|
+ - Add badges
|
189
|
+
|
190
|
+ ## [0.2.1]
|
191
|
+ ### Other
|
192
|
+ - Add badges (#28)
|
193
|
+ - Merge pull request #26 from curiosum-dev/chore/update-readme
|
194
|
+
|
195
|
+ Update README to match Kanta version
|
196
|
+ - Update README to match Kanta version
|
197
|
+
|
198
|
+ ## [0.2.0]
|
199
|
+ ### Other
|
200
|
+ - Merge pull request #25 from curiosum-dev/release/0.2.0
|
201
|
+
|
202
|
+ Release/0.2.0
|
203
|
+ - Merge pull request #24 from curiosum-dev/feature/set-docs-entry-and-bump-mix-version
|
204
|
+
|
205
|
+ Set docs entry and bump mix version
|
206
|
+ - Set docs entry and bump mix version
|
207
|
+ - Merge pull request #23 from curiosum-dev/fix/plural-translations-form
|
208
|
+
|
209
|
+ Improve UI and fix plural translations form
|
210
|
+ - Use truncate CSS prop instead of String.slice
|
211
|
+ - Improve UI and plural translations form
|
212
|
+ - Merge pull request #22 from curiosum-dev/chore/update-gettext-repo
|
213
|
+
|
214
|
+ Update gettext repo
|
215
|
+ - Update gettext repo
|
216
|
+ - Merge pull request #20 from curiosum-dev/chore/update-demo-link
|
217
|
+
|
218
|
+ Update demo link in README
|
219
|
+ - Update demo link in README
|
220
|
+ - Merge pull request #18 from curiosum-dev/feature/plugin-docs
|
221
|
+
|
222
|
+ Feature/plugin docs
|
223
|
+ - Module names consistency, explicit ArgumentError rescue
|
224
|
+ - Add dialyzer, fix credo and dialyzer warnings
|
225
|
+ - Update "How to write plugins?" tutorial
|
226
|
+ - Plugin docs, specs and conditional components rendering
|
227
|
+ - 🐛 Hotfix wrong type
|
228
|
+
|
229
|
+ It was a Map all along
|
230
|
+ - Merge pull request #14 from curiosum-dev/bugfix/prerelease-fixes
|
231
|
+
|
232
|
+ Bugfix/prerelease fixes
|
233
|
+ - 🔧 Fallback to public prefix for messages
|
234
|
+ - 🔧 Pass on_mount option to live_session opts
|
235
|
+ - 🐛 Use https instead ssh connection for gettext
|
236
|
+ - 🐛 Fix down migration order
|
237
|
+ - 🐛 Fix prefix() calls into prefix
|
238
|
+ - 🔧 Pass down opts to respective up&down migrations
|
239
|
+ - 🐛 Add limit 1
|
240
|
+ - ❄ Add devenv
|
241
|
+ - fix: multitenancy projects issues
|
242
|
+ - feat: rework plural messages handling
|
243
|
+ - Merge pull request #12 from curiosum-dev/feature/kanta-plugins-extraction
|
244
|
+
|
245
|
+ feat: extract DeepL plugin to the external package
|
246
|
+ - feat: extract DeepL plugin to the external package
|
247
|
+ - Merge pull request #11 from curiosum-dev/fix/phoenix-verified-url
|
248
|
+
|
249
|
+ fix: change unverified url to unverified path
|
250
|
+ - fix: change unverified url to unverified path
|
251
|
+ - Merge pull request #10 from curiosum-dev/fix/phoenix-verified-paths
|
252
|
+
|
253
|
+ fix: Phoenix VerifiedRoutes issues & missing views
|
254
|
+ - fix: Phoenix VerifiedRoutes issues & missing views
|
255
|
+ - Merge pull request #8 from curiosum-dev/fix/kanta-in-mix-release-setup
|
256
|
+
|
257
|
+ fix: Kanta in a mix release projects
|
258
|
+ - fix: Kanta in a mix release projects
|
259
|
+ - Merge tag 0.1.0 into develop
|
260
|
+
|
261
|
+ Initial version of Kanta
|
262
|
+
|
263
|
+ ## [0.1.0]
|
264
|
+ ### Other
|
265
|
+ - Merge branch release/0.1.0 into main
|
266
|
+ - chore: bump version
|
267
|
+ - docs: update readme
|
268
|
+ - feat: basic rwd
|
269
|
+ - feat: dark mode
|
270
|
+ - feat: add messages filters
|
271
|
+ - feat: plugins management
|
272
|
+ - feat: small refactoring
|
273
|
+ - fix: paths resolution
|
274
|
+ - feat: add gettext contexts & fix plural translations
|
275
|
+ - feat: mainly UI adjusts
|
276
|
+ - Merge pull request #6 from curiosum-dev/1-fix_github_dependency_on_gettext_fork
|
277
|
+
|
278
|
+ Fix github dependency on gettext fork (#1)
|
279
|
+ - Fix github dependency on gettext fork (#1)
|
280
|
+ - Merge tag 0.0.1-rc1 into develop
|
281
|
+
|
282
|
+ First release candidate of Kanta translations manager.
|
283
|
+ - Merge branch release/0.0.1-rc1
|
284
|
+ - chore: update package version
|
285
|
+ - Merge pull request #4 from curiosum-dev/feature/KNT-2
|
286
|
+
|
287
|
+ fix: cached translations in UI
|
288
|
+ - fix: cached translations in UI
|
289
|
+ - Merge pull request #3 from curiosum-dev/feature/KNT-1
|
290
|
+
|
291
|
+ KNT-1 mimic LiveDashboard mechanism in Kanta
|
292
|
+ - fix: LV redirect loop
|
293
|
+ - feat: reverse engineering of live dashboard (WIP)
|
294
|
+ - feat: adjust gettext repo for plural messages handling
|
295
|
+ - feat: add form for plural translation updates
|
296
|
+ - feat: add form for singular translation updates
|
297
|
+ - Merge pull request #1 from curiosum-dev/feature/petal_init
|
298
|
+
|
299
|
+ PETAL stack init & db structure rework
|
300
|
+ - feat: rework translation listings
|
301
|
+ - feat: add plural translations
|
302
|
+ - feat: add nebulex as a caching tool
|
303
|
+ - feat: add sample pages in petal
|
304
|
+ - feat: add basic configuration for semi-PETAL stack
|
305
|
+ - chore: update deps & readme
|
306
|
+ - Change translations to singular translations
|
307
|
+ - Create PopulateCacheWithStoredDataService
|
308
|
+ - Proof of concept
|
309
|
+ - Create file structure
|
310
|
+ - First commit
|
311
|
+
|
changed
README.md
|
@@ -61,6 +61,7 @@
|
61
61
|
<ul>
|
62
62
|
<li><a href="#po-writer">PO Writer</a></li>
|
63
63
|
<li><a href="#deepl">DeepL</a></li>
|
64
|
+ <li><a href="#kantasync">Translation synchronization</a></li>
|
64
65
|
</ul>
|
65
66
|
</li>
|
66
67
|
<li><a href="#roadmap">Roadmap</a></li>
|
|
@@ -102,7 +103,7 @@ by adding `kanta` to your list of dependencies in `mix.exs`:
|
102
103
|
```elixir
|
103
104
|
def deps do
|
104
105
|
[
|
105
|
- {:kanta, "~> 0.3.1"},
|
106
|
+ {:kanta, "~> 0.4.0"},
|
106
107
|
{:gettext, git: "[email protected]:ravensiris/gettext.git", branch: "runtime-gettext"}
|
107
108
|
]
|
108
109
|
end
|
|
@@ -336,6 +337,8 @@ Distributed under the MIT License. See `LICENSE.txt` for more information.
|
336
337
|
|
337
338
|
## Contact
|
338
339
|
|
340
|
+ [Curiosum](https://curiosum.com)
|
341
|
+
|
339
342
|
Michał Buszkiewicz - [email protected]
|
340
343
|
|
341
344
|
Krzysztof Janiec - [email protected]
|
changed
hex_metadata.config
|
@@ -1,6 +1,6 @@
|
1
1
|
{<<"links">>,[{<<"GitHub">>,<<"https://github.com/curiosum-dev/kanta">>}]}.
|
2
2
|
{<<"name">>,<<"kanta">>}.
|
3
|
- {<<"version">>,<<"0.3.1">>}.
|
3
|
+ {<<"version">>,<<"0.4.0">>}.
|
4
4
|
{<<"description">>,
|
5
5
|
<<"User-friendly translations manager for Elixir/Phoenix projects.">>}.
|
6
6
|
{<<"elixir">>,<<"~> 1.14">>}.
|
|
@@ -12,15 +12,21 @@
|
12
12
|
<<"lib/kanta/migrations/postgresql">>,
|
13
13
|
<<"lib/kanta/migrations/postgresql/v01.ex">>,
|
14
14
|
<<"lib/kanta/migrations/postgresql/v02.ex">>,
|
15
|
- <<"lib/kanta/migrations/postgresql.ex">>,<<"lib/kanta/translations.ex">>,
|
15
|
+ <<"lib/kanta/migrations/postgresql/v03.ex">>,
|
16
|
+ <<"lib/kanta/migrations/postgresql.ex">>,
|
17
|
+ <<"lib/kanta/migrations/sqlite3.ex">>,<<"lib/kanta/migrations/sqlite3">>,
|
18
|
+ <<"lib/kanta/migrations/sqlite3/v01.ex">>,
|
19
|
+ <<"lib/kanta/migrations/sqlite3/v02.ex">>,<<"lib/kanta/translations.ex">>,
|
16
20
|
<<"lib/kanta/cache.ex">>,<<"lib/kanta/types.ex">>,<<"lib/kanta/specs">>,
|
17
21
|
<<"lib/kanta/specs/schemata_spec.ex">>,<<"lib/kanta/validator.ex">>,
|
18
22
|
<<"lib/kanta/config.ex">>,<<"lib/kanta/utils">>,
|
19
23
|
<<"lib/kanta/utils/database_populator.ex">>,
|
20
|
- <<"lib/kanta/utils/get_schemata.ex">>,<<"lib/kanta/utils/module_utils.ex">>,
|
24
|
+ <<"lib/kanta/utils/get_schemata.ex">>,
|
25
|
+ <<"lib/kanta/utils/param_parsers.ex">>,<<"lib/kanta/utils/colors.ex">>,
|
26
|
+ <<"lib/kanta/utils/compilation.ex">>,<<"lib/kanta/utils/module_utils.ex">>,
|
21
27
|
<<"lib/kanta/query.ex">>,<<"lib/kanta/migration.ex">>,
|
22
28
|
<<"lib/kanta/gettext">>,<<"lib/kanta/gettext/repo.ex">>,
|
23
|
- <<"lib/kanta/po_files">>,
|
29
|
+ <<"lib/kanta/schema.ex">>,<<"lib/kanta/po_files">>,
|
24
30
|
<<"lib/kanta/po_files/messages_extractor_agent.ex">>,
|
25
31
|
<<"lib/kanta/po_files/handlers">>,
|
26
32
|
<<"lib/kanta/po_files/handlers/messages_extractor.ex">>,
|
|
@@ -30,6 +36,7 @@
|
30
36
|
<<"lib/kanta/po_files/services/extract_plural_translation.ex">>,
|
31
37
|
<<"lib/kanta/application.ex">>,<<"lib/kanta/repo.ex">>,
|
32
38
|
<<"lib/kanta/translations">>,
|
39
|
+ <<"lib/kanta/translations/application_source.ex">>,
|
33
40
|
<<"lib/kanta/translations/plural_translation">>,
|
34
41
|
<<"lib/kanta/translations/plural_translation/plural_translations.ex">>,
|
35
42
|
<<"lib/kanta/translations/plural_translation/finders">>,
|
|
@@ -40,6 +47,7 @@
|
40
47
|
<<"lib/kanta/translations/messages">>,
|
41
48
|
<<"lib/kanta/translations/messages/message_spec.ex">>,
|
42
49
|
<<"lib/kanta/translations/messages/finders">>,
|
50
|
+ <<"lib/kanta/translations/messages/finders/list_all_messages.ex">>,
|
43
51
|
<<"lib/kanta/translations/messages/finders/get_message.ex">>,
|
44
52
|
<<"lib/kanta/translations/messages/finders/list_messages.ex">>,
|
45
53
|
<<"lib/kanta/translations/messages/messages.ex">>,
|
|
@@ -58,9 +66,16 @@
|
58
66
|
<<"lib/kanta/translations/context">>,
|
59
67
|
<<"lib/kanta/translations/context/contexts.ex">>,
|
60
68
|
<<"lib/kanta/translations/context/finders">>,
|
69
|
+ <<"lib/kanta/translations/context/finders/list_all_contexts.ex">>,
|
61
70
|
<<"lib/kanta/translations/context/finders/get_context.ex">>,
|
62
71
|
<<"lib/kanta/translations/context/finders/list_contexts.ex">>,
|
63
72
|
<<"lib/kanta/translations/context/context_spec.ex">>,
|
73
|
+ <<"lib/kanta/translations/application_source">>,
|
74
|
+ <<"lib/kanta/translations/application_source/application_sources.ex">>,
|
75
|
+ <<"lib/kanta/translations/application_source/finders">>,
|
76
|
+ <<"lib/kanta/translations/application_source/finders/get_application_source.ex">>,
|
77
|
+ <<"lib/kanta/translations/application_source/finders/list_application_sources.ex">>,
|
78
|
+ <<"lib/kanta/translations/application_source/application_source_spec.ex">>,
|
64
79
|
<<"lib/kanta/translations/plural_translation.ex">>,
|
65
80
|
<<"lib/kanta/translations/singular_translation.ex">>,
|
66
81
|
<<"lib/kanta/translations/domain.ex">>,
|
|
@@ -70,6 +85,7 @@
|
70
85
|
<<"lib/kanta/translations/domain/finders">>,
|
71
86
|
<<"lib/kanta/translations/domain/finders/list_domains.ex">>,
|
72
87
|
<<"lib/kanta/translations/domain/finders/get_domain.ex">>,
|
88
|
+ <<"lib/kanta/translations/domain/finders/list_all_domains.ex">>,
|
73
89
|
<<"lib/kanta/translations/singular_translation">>,
|
74
90
|
<<"lib/kanta/translations/singular_translation/singular_translations.ex">>,
|
75
91
|
<<"lib/kanta/translations/singular_translation/finders">>,
|
|
@@ -106,6 +122,9 @@
|
106
122
|
<<"lib/kanta_web/live/translations/domain_live">>,
|
107
123
|
<<"lib/kanta_web/live/translations/domain_live/domain_live.html.heex">>,
|
108
124
|
<<"lib/kanta_web/live/translations/domain_live/domain_live.ex">>,
|
125
|
+ <<"lib/kanta_web/live/translations/application_source_form_live">>,
|
126
|
+ <<"lib/kanta_web/live/translations/application_source_form_live/application_source_form_live.html.heex">>,
|
127
|
+ <<"lib/kanta_web/live/translations/application_source_form_live/application_source_form_live.ex">>,
|
109
128
|
<<"lib/kanta_web/live/translations/translations_live">>,
|
110
129
|
<<"lib/kanta_web/live/translations/translations_live/translations_live.ex">>,
|
111
130
|
<<"lib/kanta_web/live/translations/translations_live/components">>,
|
|
@@ -119,6 +138,13 @@
|
119
138
|
<<"lib/kanta_web/live/translations/locales_live">>,
|
120
139
|
<<"lib/kanta_web/live/translations/locales_live/locales_live.ex">>,
|
121
140
|
<<"lib/kanta_web/live/translations/locales_live/locales_live.html.heex">>,
|
141
|
+ <<"lib/kanta_web/live/translations/application_sources_live">>,
|
142
|
+ <<"lib/kanta_web/live/translations/application_sources_live/application_sources_live.html.heex">>,
|
143
|
+ <<"lib/kanta_web/live/translations/application_sources_live/components">>,
|
144
|
+ <<"lib/kanta_web/live/translations/application_sources_live/components/application_sources_table">>,
|
145
|
+ <<"lib/kanta_web/live/translations/application_sources_live/components/application_sources_table/application_sources_table.html.heex">>,
|
146
|
+ <<"lib/kanta_web/live/translations/application_sources_live/components/application_sources_table/application_sources_table.ex">>,
|
147
|
+ <<"lib/kanta_web/live/translations/application_sources_live/application_sources_live.ex">>,
|
122
148
|
<<"lib/kanta_web/live/translations/contexts_live">>,
|
123
149
|
<<"lib/kanta_web/live/translations/contexts_live/components">>,
|
124
150
|
<<"lib/kanta_web/live/translations/contexts_live/components/contexts_table">>,
|
|
@@ -155,6 +181,7 @@
|
155
181
|
<<"lib/kanta_web/controllers/api/singular_translations_controller.ex">>,
|
156
182
|
<<"lib/kanta_web/controllers/api/locales_controller.ex">>,
|
157
183
|
<<"lib/kanta_web/controllers/api/messages_controller.ex">>,
|
184
|
+ <<"lib/kanta_web/controllers/api/application_sources_controller.ex">>,
|
158
185
|
<<"lib/kanta_web/views">>,<<"lib/kanta_web/views/layout_view.ex">>,
|
159
186
|
<<"lib/kanta.ex">>,<<"priv">>,<<"priv/iso639.json">>,<<"dist">>,
|
160
187
|
<<"dist/css">>,<<"dist/css/app.css">>,<<"dist/js">>,<<"dist/js/app.js">>,
|
|
@@ -168,12 +195,12 @@
|
168
195
|
[{<<"name">>,<<"ecto">>},
|
169
196
|
{<<"app">>,<<"ecto">>},
|
170
197
|
{<<"optional">>,false},
|
171
|
- {<<"requirement">>,<<"~> 3.10">>},
|
198
|
+ {<<"requirement">>,<<"~> 3.12">>},
|
172
199
|
{<<"repository">>,<<"hexpm">>}],
|
173
200
|
[{<<"name">>,<<"ecto_sql">>},
|
174
201
|
{<<"app">>,<<"ecto_sql">>},
|
175
202
|
{<<"optional">>,false},
|
176
|
- {<<"requirement">>,<<"~> 3.10">>},
|
203
|
+ {<<"requirement">>,<<"~> 3.12">>},
|
177
204
|
{<<"repository">>,<<"hexpm">>}],
|
178
205
|
[{<<"name">>,<<"phoenix">>},
|
179
206
|
{<<"app">>,<<"phoenix">>},
|
|
@@ -190,6 +217,16 @@
|
190
217
|
{<<"optional">>,false},
|
191
218
|
{<<"requirement">>,<<"~> 0.20">>},
|
192
219
|
{<<"repository">>,<<"hexpm">>}],
|
220
|
+ [{<<"name">>,<<"phoenix_html">>},
|
221
|
+ {<<"app">>,<<"phoenix_html">>},
|
222
|
+ {<<"optional">>,false},
|
223
|
+ {<<"requirement">>,<<"~> 4.0">>},
|
224
|
+ {<<"repository">>,<<"hexpm">>}],
|
225
|
+ [{<<"name">>,<<"phoenix_html_helpers">>},
|
226
|
+ {<<"app">>,<<"phoenix_html_helpers">>},
|
227
|
+ {<<"optional">>,false},
|
228
|
+ {<<"requirement">>,<<"~> 1.0">>},
|
229
|
+ {<<"repository">>,<<"hexpm">>}],
|
193
230
|
[{<<"name">>,<<"tailwind">>},
|
194
231
|
{<<"app">>,<<"tailwind">>},
|
195
232
|
{<<"optional">>,false},
|
|
@@ -223,6 +260,16 @@
|
223
260
|
[{<<"name">>,<<"uri_query">>},
|
224
261
|
{<<"app">>,<<"uri_query">>},
|
225
262
|
{<<"optional">>,false},
|
226
|
- {<<"requirement">>,<<"~> 0.1.1">>},
|
263
|
+ {<<"requirement">>,<<"~> 0.2">>},
|
264
|
+ {<<"repository">>,<<"hexpm">>}],
|
265
|
+ [{<<"name">>,<<"versioce">>},
|
266
|
+ {<<"app">>,<<"versioce">>},
|
267
|
+ {<<"optional">>,false},
|
268
|
+ {<<"requirement">>,<<"~> 2.0.0">>},
|
269
|
+ {<<"repository">>,<<"hexpm">>}],
|
270
|
+ [{<<"name">>,<<"git_cli">>},
|
271
|
+ {<<"app">>,<<"git_cli">>},
|
272
|
+ {<<"optional">>,false},
|
273
|
+ {<<"requirement">>,<<"~> 0.3.0">>},
|
227
274
|
{<<"repository">>,<<"hexpm">>}]]}.
|
228
275
|
{<<"build_tools">>,[<<"mix">>]}.
|
changed
lib/kanta/config.ex
|
@@ -8,7 +8,8 @@ defmodule Kanta.Config do
|
8
8
|
repo: module(),
|
9
9
|
endpoint: module(),
|
10
10
|
plugins: false | [module() | {module() | Keyword.t()}],
|
11
|
- disable_api_authorization: boolean()
|
11
|
+ disable_api_authorization: boolean(),
|
12
|
+ id_parse_function: mfa() | (term() -> {:ok, term()} | term())
|
12
13
|
}
|
13
14
|
|
14
15
|
defstruct name: Kanta,
|
|
@@ -16,7 +17,8 @@ defmodule Kanta.Config do
|
16
17
|
repo: nil,
|
17
18
|
endpoint: nil,
|
18
19
|
plugins: [],
|
19
|
- disable_api_authorization: false
|
20
|
+ disable_api_authorization: false,
|
21
|
+ id_parse_function: {Kanta.Utils.ParamParsers, :default_id_parser, 1}
|
20
22
|
|
21
23
|
alias Kanta.Validator
|
22
24
|
|
|
@@ -82,6 +84,29 @@ defmodule Kanta.Config do
|
82
84
|
end
|
83
85
|
end
|
84
86
|
|
87
|
+ defp validate_opt(_opts, {:id_parse_function, {module, function, 1} = id_parse_function}) do
|
88
|
+ if Code.ensure_loaded?(module) and Kernel.function_exported?(module, function, 1) do
|
89
|
+ :ok
|
90
|
+ else
|
91
|
+ {:error,
|
92
|
+ "expected :id_parse_function to be a function with arity of 1, got: #{inspect(id_parse_function)}"}
|
93
|
+ end
|
94
|
+ end
|
95
|
+
|
96
|
+ defp validate_opt(_opts, {:id_parse_function, {_module, _function, _arity} = id_parse_function}) do
|
97
|
+ {:error,
|
98
|
+ "expected :id_parse_function to be a function with arity of 1, got: #{inspect(id_parse_function)}"}
|
99
|
+ end
|
100
|
+
|
101
|
+ defp validate_opt(_opts, {:id_parse_function, id_parse_function}) do
|
102
|
+ if is_function(id_parse_function, 1) do
|
103
|
+ :ok
|
104
|
+ else
|
105
|
+ {:error,
|
106
|
+ "expected :id_parse_function to be a function with arity of 1, got: #{inspect(id_parse_function)}"}
|
107
|
+ end
|
108
|
+ end
|
109
|
+
|
85
110
|
defp validate_opt(_opts, option) do
|
86
111
|
{:unknown, option, __MODULE__}
|
87
112
|
end
|
changed
lib/kanta/gettext/repo.ex
|
@@ -1,4 +1,6 @@
|
1
1
|
defmodule Kanta.Gettext.Repo do
|
2
|
+ alias Kanta.Utils.Compilation
|
3
|
+
|
2
4
|
alias Kanta.Translations.{
|
3
5
|
Context,
|
4
6
|
Domain,
|
|
@@ -15,6 +17,14 @@ defmodule Kanta.Gettext.Repo do
|
15
17
|
end
|
16
18
|
|
17
19
|
def get_translation(locale, domain, msgctxt, msgid, opts) do
|
20
|
+ if Compilation.compiling?() do
|
21
|
+ msgid
|
22
|
+ else
|
23
|
+ do_get_translation(locale, domain, msgctxt, msgid, opts)
|
24
|
+ end
|
25
|
+ end
|
26
|
+
|
27
|
+ defp do_get_translation(locale, domain, msgctxt, msgid, opts) do
|
18
28
|
default_locale = Application.get_env(:kanta, :default_locale) || "en"
|
19
29
|
|
20
30
|
with {:ok, %Locale{id: locale_id}} <-
|
|
@@ -27,7 +37,8 @@ defmodule Kanta.Gettext.Repo do
|
27
37
|
filter: [
|
28
38
|
msgid: msgid,
|
29
39
|
context_id: context_id,
|
30
|
- domain_id: domain_id
|
40
|
+ domain_id: domain_id,
|
41
|
+ application_source_id: nil
|
31
42
|
]
|
32
43
|
),
|
33
44
|
{:ok, %SingularTranslation{translated_text: text}} <-
|
|
@@ -39,7 +50,7 @@ defmodule Kanta.Gettext.Repo do
|
39
50
|
) do
|
40
51
|
if is_nil(text) do
|
41
52
|
if locale != default_locale do
|
42
|
- get_translation(default_locale, domain, msgctxt, msgid, opts)
|
53
|
+ do_get_translation(default_locale, domain, msgctxt, msgid, opts)
|
43
54
|
else
|
44
55
|
:not_found
|
45
56
|
end
|
|
@@ -61,6 +72,30 @@ defmodule Kanta.Gettext.Repo do
|
61
72
|
plural_form,
|
62
73
|
opts
|
63
74
|
) do
|
75
|
+ if Compilation.compiling?() do
|
76
|
+ if plural_form == 1, do: msgid, else: msgid_plural
|
77
|
+ else
|
78
|
+ do_get_plural_translation(
|
79
|
+ locale,
|
80
|
+ domain,
|
81
|
+ msgctxt,
|
82
|
+ msgid,
|
83
|
+ msgid_plural,
|
84
|
+ plural_form,
|
85
|
+ opts
|
86
|
+ )
|
87
|
+ end
|
88
|
+ end
|
89
|
+
|
90
|
+ defp do_get_plural_translation(
|
91
|
+ locale,
|
92
|
+ domain,
|
93
|
+ msgctxt,
|
94
|
+ msgid,
|
95
|
+ msgid_plural,
|
96
|
+ plural_form,
|
97
|
+ opts
|
98
|
+ ) do
|
64
99
|
default_locale = Application.get_env(:kanta, :default_locale) || "en"
|
65
100
|
|
66
101
|
with {:ok, %Locale{id: locale_id, plurals_header: plurals_header}} <-
|
|
@@ -73,7 +108,8 @@ defmodule Kanta.Gettext.Repo do
|
73
108
|
filter: [
|
74
109
|
msgid: msgid_plural,
|
75
110
|
context_id: context_id,
|
76
|
- domain_id: domain_id
|
111
|
+ domain_id: domain_id,
|
112
|
+ application_source_id: nil
|
77
113
|
]
|
78
114
|
),
|
79
115
|
{:ok, plurals_options} <- Expo.PluralForms.parse(plurals_header),
|
|
@@ -88,7 +124,7 @@ defmodule Kanta.Gettext.Repo do
|
88
124
|
) do
|
89
125
|
if is_nil(text) do
|
90
126
|
if locale != default_locale do
|
91
|
- get_plural_translation(
|
127
|
+ do_get_plural_translation(
|
92
128
|
default_locale,
|
93
129
|
domain,
|
94
130
|
msgctxt,
|
changed
lib/kanta/migration.ex
|
@@ -193,6 +193,7 @@ defmodule Kanta.Migration do
|
193
193
|
defp migrator do
|
194
194
|
case repo().__adapter__() do
|
195
195
|
Ecto.Adapters.Postgres -> Kanta.Migrations.Postgresql
|
196
|
+ Ecto.Adapters.SQLite3 -> Kanta.Migrations.SQLite3
|
196
197
|
end
|
197
198
|
end
|
198
199
|
end
|
changed
lib/kanta/migrations/postgresql.ex
|
@@ -6,7 +6,7 @@ defmodule Kanta.Migrations.Postgresql do
|
6
6
|
use Ecto.Migration
|
7
7
|
|
8
8
|
@initial_version 1
|
9
|
- @current_version 2
|
9
|
+ @current_version 3
|
10
10
|
@default_prefix "public"
|
11
11
|
|
12
12
|
@doc false
|
changed
lib/kanta/migrations/postgresql/v01.ex
|
@@ -5,6 +5,8 @@ defmodule Kanta.Migrations.Postgresql.V01 do
|
5
5
|
|
6
6
|
use Ecto.Migration
|
7
7
|
|
8
|
+ alias Kanta.Utils.Colors
|
9
|
+
|
8
10
|
@default_prefix "public"
|
9
11
|
@kanta_locales "kanta_locales"
|
10
12
|
@kanta_domains "kanta_domains"
|
|
@@ -56,7 +58,7 @@ defmodule Kanta.Migrations.Postgresql.V01 do
|
56
58
|
create_if_not_exists table(@kanta_domains) do
|
57
59
|
add(:name, :string)
|
58
60
|
add(:description, :text)
|
59
|
- add(:color, :string, null: false, default: "#7E37D8")
|
61
|
+ add(:color, :string, null: false, default: Colors.default_color())
|
60
62
|
timestamps()
|
61
63
|
end
|
62
64
|
|
|
@@ -67,7 +69,7 @@ defmodule Kanta.Migrations.Postgresql.V01 do
|
67
69
|
create_if_not_exists table(@kanta_contexts) do
|
68
70
|
add(:name, :string)
|
69
71
|
add(:description, :text)
|
70
|
- add(:color, :string, null: false, default: "#7E37D8")
|
72
|
+ add(:color, :string, null: false, default: Colors.default_color())
|
71
73
|
timestamps()
|
72
74
|
end
|
added
lib/kanta/migrations/postgresql/v03.ex
|
@@ -0,0 +1,81 @@
|
1
|
+ defmodule Kanta.Migrations.Postgresql.V03 do
|
2
|
+ @moduledoc """
|
3
|
+ Kanta V3 Migrations
|
4
|
+ """
|
5
|
+
|
6
|
+ use Ecto.Migration
|
7
|
+
|
8
|
+ alias Kanta.Utils.Colors
|
9
|
+
|
10
|
+ @kanta_application_sources "kanta_application_sources"
|
11
|
+ @kanta_messages "kanta_messages"
|
12
|
+
|
13
|
+ def up(opts) do
|
14
|
+ Kanta.Migration.up(version: 2)
|
15
|
+
|
16
|
+ [
|
17
|
+ &up_application_sources/1,
|
18
|
+ &up_kanta_messages/1
|
19
|
+ ]
|
20
|
+ |> Enum.each(&apply(&1, [opts]))
|
21
|
+ end
|
22
|
+
|
23
|
+ def down(opts) do
|
24
|
+ [
|
25
|
+ &down_application_sources/1,
|
26
|
+ &down_kanta_messages/1
|
27
|
+ ]
|
28
|
+ |> Enum.each(&apply(&1, [opts]))
|
29
|
+
|
30
|
+ Kanta.Migration.down(version: 2)
|
31
|
+ end
|
32
|
+
|
33
|
+ def up_application_sources(_opts) do
|
34
|
+ create_if_not_exists table(@kanta_application_sources) do
|
35
|
+ add(:name, :string)
|
36
|
+ add(:description, :text)
|
37
|
+ add(:color, :string, null: false, default: Colors.default_color())
|
38
|
+ timestamps()
|
39
|
+ end
|
40
|
+
|
41
|
+ create_if_not_exists unique_index(@kanta_application_sources, [:name])
|
42
|
+ end
|
43
|
+
|
44
|
+ def up_kanta_messages(_opts) do
|
45
|
+ alter table(@kanta_messages) do
|
46
|
+ add(:application_source_id, references(@kanta_application_sources), null: true)
|
47
|
+ end
|
48
|
+
|
49
|
+ drop unique_index(@kanta_messages, [:context_id, :domain_id, :msgid])
|
50
|
+
|
51
|
+ create_if_not_exists unique_index(
|
52
|
+ @kanta_messages,
|
53
|
+ [
|
54
|
+ :application_source_id,
|
55
|
+ :context_id,
|
56
|
+ :domain_id,
|
57
|
+ :msgid
|
58
|
+ ],
|
59
|
+ nulls_distinct: false
|
60
|
+ )
|
61
|
+ end
|
62
|
+
|
63
|
+ def down_application_sources(_opts) do
|
64
|
+ drop table(@kanta_application_sources)
|
65
|
+ end
|
66
|
+
|
67
|
+ def down_kanta_messages(_opts) do
|
68
|
+ drop unique_index(@kanta_messages, [
|
69
|
+ :application_source_id,
|
70
|
+ :context_id,
|
71
|
+ :domain_id,
|
72
|
+ :msgid
|
73
|
+ ])
|
74
|
+
|
75
|
+ create_if_not_exists unique_index(@kanta_messages, [:context_id, :domain_id, :msgid])
|
76
|
+
|
77
|
+ alter table(@kanta_messages) do
|
78
|
+ remove(:application_source_id)
|
79
|
+ end
|
80
|
+ end
|
81
|
+ end
|
added
lib/kanta/migrations/sqlite3.ex
|
@@ -0,0 +1,81 @@
|
1
|
+ defmodule Kanta.Migrations.SQLite3 do
|
2
|
+ @moduledoc false
|
3
|
+
|
4
|
+ @behaviour Kanta.Migration
|
5
|
+
|
6
|
+ use Ecto.Migration
|
7
|
+
|
8
|
+ @initial_version 1
|
9
|
+ @current_version 2
|
10
|
+
|
11
|
+ @doc false
|
12
|
+ def initial_version, do: @initial_version
|
13
|
+
|
14
|
+ @doc false
|
15
|
+ def current_version, do: @current_version
|
16
|
+
|
17
|
+ @impl Kanta.Migration
|
18
|
+ def up(opts) do
|
19
|
+ opts = with_defaults(opts, @current_version)
|
20
|
+ initial = migrated_version(opts)
|
21
|
+
|
22
|
+ cond do
|
23
|
+ initial == 0 ->
|
24
|
+ change(@initial_version..opts.version, :up, opts)
|
25
|
+
|
26
|
+ initial < opts.version ->
|
27
|
+ change((initial + 1)..opts.version, :up, opts)
|
28
|
+
|
29
|
+ true ->
|
30
|
+ :ok
|
31
|
+ end
|
32
|
+ end
|
33
|
+
|
34
|
+ @impl Kanta.Migration
|
35
|
+ def down(opts) do
|
36
|
+ opts = with_defaults(opts, @initial_version)
|
37
|
+ initial = max(migrated_version(opts), @initial_version)
|
38
|
+
|
39
|
+ if initial >= opts.version do
|
40
|
+ change(initial..opts.version, :down, opts)
|
41
|
+ end
|
42
|
+ end
|
43
|
+
|
44
|
+ @impl Kanta.Migration
|
45
|
+ def migrated_version(opts) do
|
46
|
+ opts = with_defaults(opts, @initial_version)
|
47
|
+
|
48
|
+ repo = Map.get_lazy(opts, :repo, fn -> repo() end)
|
49
|
+ query = "PRAGMA user_version"
|
50
|
+
|
51
|
+ case repo.query(query, [], log: false) do
|
52
|
+ {:ok, %{rows: [[version]]}} when is_integer(version) -> version
|
53
|
+ _ -> 0
|
54
|
+ end
|
55
|
+ end
|
56
|
+
|
57
|
+ defp change(range, direction, opts) do
|
58
|
+ for index <- range do
|
59
|
+ pad_idx = String.pad_leading(to_string(index), 2, "0")
|
60
|
+
|
61
|
+ [__MODULE__, "V#{pad_idx}"]
|
62
|
+ |> Module.concat()
|
63
|
+ |> apply(direction, [opts])
|
64
|
+ end
|
65
|
+
|
66
|
+ case direction do
|
67
|
+ :up -> record_version(opts, Enum.max(range))
|
68
|
+ :down -> record_version(opts, Enum.min(range) - 1)
|
69
|
+ end
|
70
|
+ end
|
71
|
+
|
72
|
+ defp record_version(_opts, 0), do: :ok
|
73
|
+
|
74
|
+ defp record_version(_opts, version) do
|
75
|
+ execute "PRAGMA user_version = #{version}"
|
76
|
+ end
|
77
|
+
|
78
|
+ defp with_defaults(opts, version) do
|
79
|
+ Enum.into(opts, %{version: version})
|
80
|
+ end
|
81
|
+ end
|
added
lib/kanta/migrations/sqlite3/v01.ex
|
@@ -0,0 +1,151 @@
|
1
|
+ defmodule Kanta.Migrations.SQLite3.V01 do
|
2
|
+ @moduledoc false
|
3
|
+
|
4
|
+ use Ecto.Migration
|
5
|
+
|
6
|
+ alias Kanta.Utils.Colors
|
7
|
+
|
8
|
+ @kanta_locales "kanta_locales"
|
9
|
+ @kanta_domains "kanta_domains"
|
10
|
+ @kanta_contexts "kanta_contexts"
|
11
|
+ @kanta_messages "kanta_messages"
|
12
|
+ @kanta_singular_translations "kanta_singular_translations"
|
13
|
+ @kanta_plural_translations "kanta_plural_translations"
|
14
|
+
|
15
|
+ def up(opts) do
|
16
|
+ [
|
17
|
+ &up_locales/1,
|
18
|
+ &up_contexts/1,
|
19
|
+ &up_domains/1,
|
20
|
+ &up_messages/1,
|
21
|
+ &up_singular_translations/1,
|
22
|
+ &up_plural_translations/1
|
23
|
+ ]
|
24
|
+ |> Enum.each(&apply(&1, [opts]))
|
25
|
+ end
|
26
|
+
|
27
|
+ def down(opts) do
|
28
|
+ [
|
29
|
+ &down_plural_translations/1,
|
30
|
+ &down_singular_translations/1,
|
31
|
+ &down_messages/1,
|
32
|
+ &down_domains/1,
|
33
|
+ &down_contexts/1,
|
34
|
+ &down_locales/1
|
35
|
+ ]
|
36
|
+ |> Enum.each(&apply(&1, [opts]))
|
37
|
+ end
|
38
|
+
|
39
|
+ defp up_locales(_opts) do
|
40
|
+ create_if_not_exists table(@kanta_locales) do
|
41
|
+ add(:iso639_code, :string)
|
42
|
+ add(:name, :string)
|
43
|
+ add(:native_name, :string)
|
44
|
+ add(:family, :string)
|
45
|
+ add(:wiki_url, :string)
|
46
|
+ add(:colors, {:array, :string})
|
47
|
+ add(:plurals_header, :string)
|
48
|
+ timestamps()
|
49
|
+ end
|
50
|
+
|
51
|
+ create_if_not_exists unique_index(@kanta_locales, [:iso639_code])
|
52
|
+ end
|
53
|
+
|
54
|
+ defp up_domains(_opts) do
|
55
|
+ create_if_not_exists table(@kanta_domains) do
|
56
|
+ add(:name, :string)
|
57
|
+ add(:description, :text)
|
58
|
+ add(:color, :string, null: false, default: Colors.default_color())
|
59
|
+ timestamps()
|
60
|
+ end
|
61
|
+
|
62
|
+ create_if_not_exists unique_index(@kanta_domains, [:name])
|
63
|
+ end
|
64
|
+
|
65
|
+ defp up_contexts(_opts) do
|
66
|
+ create_if_not_exists table(@kanta_contexts) do
|
67
|
+ add(:name, :string)
|
68
|
+ add(:description, :text)
|
69
|
+ add(:color, :string, null: false, default: Colors.default_color())
|
70
|
+ timestamps()
|
71
|
+ end
|
72
|
+
|
73
|
+ create_if_not_exists unique_index(@kanta_contexts, [:name])
|
74
|
+ end
|
75
|
+
|
76
|
+ defp up_messages(_opts) do
|
77
|
+ create_if_not_exists table(@kanta_messages) do
|
78
|
+ add(:msgid, :text)
|
79
|
+
|
80
|
+ add(:message_type, :string,
|
81
|
+ null: false,
|
82
|
+ check: %{name: "message_type_check", expr: "message_type IN ('singular', 'plural')"}
|
83
|
+ )
|
84
|
+
|
85
|
+ add(:domain_id, references(@kanta_domains), null: true)
|
86
|
+ add(:context_id, references(@kanta_contexts), null: true)
|
87
|
+ timestamps()
|
88
|
+ end
|
89
|
+
|
90
|
+ execute "ALTER TABLE #{@kanta_messages} ADD COLUMN searchable TEXT AS (msgid) VIRTUAL"
|
91
|
+
|
92
|
+ create_if_not_exists unique_index(@kanta_messages, [:context_id, :domain_id, :msgid])
|
93
|
+ end
|
94
|
+
|
95
|
+ defp up_singular_translations(_opts) do
|
96
|
+ create_if_not_exists table(@kanta_singular_translations) do
|
97
|
+ add(:original_text, :text)
|
98
|
+ add(:translated_text, :text, null: true)
|
99
|
+ add(:locale_id, references(@kanta_locales))
|
100
|
+ add(:message_id, references(@kanta_messages))
|
101
|
+ timestamps()
|
102
|
+ end
|
103
|
+
|
104
|
+ create_if_not_exists unique_index(@kanta_singular_translations, [:locale_id, :message_id])
|
105
|
+
|
106
|
+ execute "ALTER TABLE #{@kanta_singular_translations} ADD COLUMN searchable TEXT AS (translated_text) VIRTUAL"
|
107
|
+ end
|
108
|
+
|
109
|
+ defp up_plural_translations(_opts) do
|
110
|
+ create_if_not_exists table(@kanta_plural_translations) do
|
111
|
+ add(:nplural_index, :integer)
|
112
|
+ add(:original_text, :text)
|
113
|
+ add(:translated_text, :text, null: true)
|
114
|
+ add(:locale_id, references(@kanta_locales))
|
115
|
+ add(:message_id, references(@kanta_messages))
|
116
|
+ timestamps()
|
117
|
+ end
|
118
|
+
|
119
|
+ create_if_not_exists unique_index(@kanta_plural_translations, [
|
120
|
+ :locale_id,
|
121
|
+ :message_id,
|
122
|
+ :nplural_index
|
123
|
+ ])
|
124
|
+
|
125
|
+ execute "ALTER TABLE #{@kanta_plural_translations} ADD COLUMN searchable TEXT AS (translated_text) VIRTUAL"
|
126
|
+ end
|
127
|
+
|
128
|
+ defp down_locales(_opts) do
|
129
|
+ drop table(@kanta_locales)
|
130
|
+ end
|
131
|
+
|
132
|
+ defp down_domains(_opts) do
|
133
|
+ drop table(@kanta_domains)
|
134
|
+ end
|
135
|
+
|
136
|
+ defp down_contexts(_opts) do
|
137
|
+ drop table(@kanta_contexts)
|
138
|
+ end
|
139
|
+
|
140
|
+ defp down_messages(_opts) do
|
141
|
+ drop table(@kanta_messages)
|
142
|
+ end
|
143
|
+
|
144
|
+ defp down_singular_translations(_opts) do
|
145
|
+ drop table(@kanta_singular_translations)
|
146
|
+ end
|
147
|
+
|
148
|
+ defp down_plural_translations(_opts) do
|
149
|
+ drop table(@kanta_plural_translations)
|
150
|
+ end
|
151
|
+ end
|
added
lib/kanta/migrations/sqlite3/v02.ex
|
@@ -0,0 +1,78 @@
|
1
|
+ defmodule Kanta.Migrations.SQLite3.V02 do
|
2
|
+ @moduledoc """
|
3
|
+ Kanta V2 Migrations
|
4
|
+ """
|
5
|
+
|
6
|
+ use Ecto.Migration
|
7
|
+ alias Kanta.Utils.Colors
|
8
|
+
|
9
|
+ @kanta_application_sources "kanta_application_sources"
|
10
|
+ @kanta_messages "kanta_messages"
|
11
|
+
|
12
|
+ def up(opts) do
|
13
|
+ [
|
14
|
+ &up_application_sources/1,
|
15
|
+ &up_kanta_messages/1
|
16
|
+ ]
|
17
|
+ |> Enum.each(&apply(&1, [opts]))
|
18
|
+ end
|
19
|
+
|
20
|
+ def down(opts) do
|
21
|
+ [
|
22
|
+ &down_application_sources/1,
|
23
|
+ &down_kanta_messages/1
|
24
|
+ ]
|
25
|
+ |> Enum.each(&apply(&1, [opts]))
|
26
|
+ end
|
27
|
+
|
28
|
+ def up_application_sources(_opts) do
|
29
|
+ create_if_not_exists table(@kanta_application_sources) do
|
30
|
+ add(:name, :string)
|
31
|
+ add(:description, :text)
|
32
|
+ add(:color, :string, null: false, default: Colors.default_color())
|
33
|
+ timestamps()
|
34
|
+ end
|
35
|
+
|
36
|
+ create_if_not_exists unique_index(@kanta_application_sources, [:name])
|
37
|
+ end
|
38
|
+
|
39
|
+ def up_kanta_messages(_opts) do
|
40
|
+ alter table(@kanta_messages) do
|
41
|
+ add(:application_source_id, references(@kanta_application_sources), null: true)
|
42
|
+ end
|
43
|
+
|
44
|
+ drop unique_index(@kanta_messages, [:context_id, :domain_id, :msgid])
|
45
|
+
|
46
|
+ create_if_not_exists unique_index(
|
47
|
+ @kanta_messages,
|
48
|
+ [
|
49
|
+ :application_source_id,
|
50
|
+ :context_id,
|
51
|
+ :domain_id,
|
52
|
+ :msgid
|
53
|
+ ]
|
54
|
+ )
|
55
|
+ end
|
56
|
+
|
57
|
+ def down_application_sources(_opts) do
|
58
|
+ drop table(@kanta_application_sources)
|
59
|
+ end
|
60
|
+
|
61
|
+ def down_kanta_messages(_opts) do
|
62
|
+ drop unique_index(
|
63
|
+ @kanta_messages,
|
64
|
+ [
|
65
|
+ :application_source_id,
|
66
|
+ :context_id,
|
67
|
+ :domain_id,
|
68
|
+ :msgid
|
69
|
+ ]
|
70
|
+ )
|
71
|
+
|
72
|
+ create_if_not_exists unique_index(@kanta_messages, [:context_id, :domain_id, :msgid])
|
73
|
+
|
74
|
+ alter table(@kanta_messages) do
|
75
|
+ remove(:application_source_id)
|
76
|
+ end
|
77
|
+ end
|
78
|
+ end
|
changed
lib/kanta/po_files/handlers/messages_extractor.ex
|
@@ -16,8 +16,8 @@ defmodule Kanta.POFiles.MessagesExtractor do
|
16
16
|
]
|
17
17
|
|
18
18
|
priv = :code.priv_dir(opts[:otp_name])
|
19
|
- all_po_files = po_files_in_priv(priv)
|
20
|
- known_po_files = known_po_files(all_po_files, opts)
|
19
|
+ priv_gettext_po_files = po_files_in_priv(priv)
|
20
|
+ known_po_files = known_po_files(priv_gettext_po_files, opts)
|
21
21
|
|
22
22
|
extract_translations(known_po_files)
|
23
23
|
end
|
|
@@ -35,35 +35,35 @@ defmodule Kanta.POFiles.MessagesExtractor do
|
35
35
|
|
36
36
|
messages
|
37
37
|
|> Stream.map(fn
|
38
|
- %Expo.Message.Singular{msgctxt: nil, msgid: [msgid], msgstr: [text]} ->
|
38
|
+ %Expo.Message.Singular{msgctxt: nil, msgid: msgid, msgstr: texts} ->
|
39
39
|
ExtractSingularTranslation.call(%{
|
40
|
- msgid: msgid,
|
40
|
+ msgid: Enum.join(msgid),
|
41
41
|
locale_name: locale,
|
42
42
|
domain_name: domain,
|
43
|
- original_text: text
|
43
|
+ original_text: Enum.join(texts)
|
44
44
|
})
|
45
45
|
|
46
|
- %Expo.Message.Singular{msgctxt: [msgctxt], msgid: [msgid], msgstr: [text]} ->
|
46
|
+ %Expo.Message.Singular{msgctxt: [msgctxt], msgid: msgid, msgstr: texts} ->
|
47
47
|
ExtractSingularTranslation.call(%{
|
48
|
- msgid: msgid,
|
48
|
+ msgid: Enum.join(msgid),
|
49
49
|
context_name: msgctxt,
|
50
50
|
locale_name: locale,
|
51
51
|
domain_name: domain,
|
52
|
- original_text: text
|
52
|
+ original_text: Enum.join(texts)
|
53
53
|
})
|
54
54
|
|
55
|
- %Expo.Message.Plural{msgctxt: nil, msgid_plural: [msgid], msgstr: plurals_map} ->
|
55
|
+ %Expo.Message.Plural{msgctxt: nil, msgid_plural: msgid, msgstr: plurals_map} ->
|
56
56
|
ExtractPluralTranslation.call(%{
|
57
|
- msgid: msgid,
|
57
|
+ msgid: Enum.join(msgid),
|
58
58
|
locale_name: locale,
|
59
59
|
domain_name: domain,
|
60
60
|
plurals_map: plurals_map,
|
61
61
|
plurals_header: plurals_header
|
62
62
|
})
|
63
63
|
|
64
|
- %Expo.Message.Plural{msgctxt: [msgctxt], msgid_plural: [msgid], msgstr: plurals_map} ->
|
64
|
+ %Expo.Message.Plural{msgctxt: [msgctxt], msgid_plural: msgid, msgstr: plurals_map} ->
|
65
65
|
ExtractPluralTranslation.call(%{
|
66
|
- msgid: msgid,
|
66
|
+ msgid: Enum.join(msgid),
|
67
67
|
context_name: msgctxt,
|
68
68
|
locale_name: locale,
|
69
69
|
domain_name: domain,
|
|
@@ -82,6 +82,7 @@ defmodule Kanta.POFiles.MessagesExtractor do
|
82
82
|
|
83
83
|
defp po_files_in_priv(priv) do
|
84
84
|
priv
|
85
|
+ |> Path.join("gettext")
|
85
86
|
|> Path.join(@po_wildcard)
|
86
87
|
|> Path.wildcard()
|
87
88
|
end
|
changed
lib/kanta/query.ex
|
@@ -44,7 +44,7 @@ defmodule Kanta.Query do
|
44
44
|
def paginate(query, page \\ 1, per_page \\ @default_page_size)
|
45
45
|
|
46
46
|
def paginate(query, page, per_page) do
|
47
|
- page = if is_number(page), do: max(page, 1), else: 1
|
47
|
+ page = parse_page(page)
|
48
48
|
|
49
49
|
per_page =
|
50
50
|
if is_number(per_page), do: max(per_page, @minimum_per_page), else: @default_page_size
|
|
@@ -61,7 +61,7 @@ defmodule Kanta.Query do
|
61
61
|
%Scrivener.Config{
|
62
62
|
caller: self(),
|
63
63
|
module: Repo.get_repo(),
|
64
|
- page_number: page || 1,
|
64
|
+ page_number: page,
|
65
65
|
page_size: per_page || @default_page_size,
|
66
66
|
options: []
|
67
67
|
}
|
|
@@ -337,10 +337,20 @@ defmodule Kanta.Query do
|
337
337
|
def search_query(query, search_term) do
|
338
338
|
repo = Repo.get_repo()
|
339
339
|
|
340
|
- if Postgresql.migrated_version(%{repo: repo}) >= 2 do
|
341
|
- search_query_fuzzy(query, search_term)
|
342
|
- else
|
343
|
- search_query_legacy(query, search_term)
|
340
|
+ case repo.__adapter__() do
|
341
|
+ Ecto.Adapters.Postgres ->
|
342
|
+ if Postgresql.migrated_version(%{repo: repo}) >= 2 do
|
343
|
+ search_query_fuzzy(query, search_term)
|
344
|
+ else
|
345
|
+ search_query_legacy(query, search_term)
|
346
|
+ end
|
347
|
+
|
348
|
+ _ ->
|
349
|
+ or_where(
|
350
|
+ query,
|
351
|
+ [{unquote(opts[:binding]), resource}],
|
352
|
+ like(resource.searchable, ^"%#{search_term}%")
|
353
|
+ )
|
344
354
|
end
|
345
355
|
end
|
346
356
|
|
|
@@ -417,6 +427,29 @@ defmodule Kanta.Query do
|
417
427
|
end
|
418
428
|
|
419
429
|
defoverridable join_resource: 3
|
430
|
+
|
431
|
+ @spec parse_page(page :: any()) :: integer()
|
432
|
+ defp parse_page(page) when is_binary(page) do
|
433
|
+ case Integer.parse(page) do
|
434
|
+ {n, _} ->
|
435
|
+ parse_page(n)
|
436
|
+
|
437
|
+ :error ->
|
438
|
+ 1
|
439
|
+ end
|
440
|
+ end
|
441
|
+
|
442
|
+ defp parse_page(page) when is_integer(page) do
|
443
|
+ max(page, 1)
|
444
|
+ end
|
445
|
+
|
446
|
+ defp parse_page(page) when is_float(page) do
|
447
|
+ page
|
448
|
+ |> floor()
|
449
|
+ |> parse_page()
|
450
|
+ end
|
451
|
+
|
452
|
+ defp parse_page(_), do: 1
|
420
453
|
end
|
421
454
|
end
|
422
455
|
end
|
added
lib/kanta/schema.ex
|
@@ -0,0 +1,13 @@
|
1
|
+ defmodule Kanta.Schema do
|
2
|
+ @moduledoc false
|
3
|
+
|
4
|
+ defmacro __using__(_opts) do
|
5
|
+ quote do
|
6
|
+ use Ecto.Schema
|
7
|
+
|
8
|
+ @primary_key {:id, Application.compile_env(:kanta, :schema_id_type, :id),
|
9
|
+ autogenerate: true}
|
10
|
+ @foreign_key_type Application.compile_env(:kanta, :schema_id_type, :id)
|
11
|
+ end
|
12
|
+ end
|
13
|
+ end
|
changed
lib/kanta/translations.ex
|
@@ -4,6 +4,7 @@ defmodule Kanta.Translations do
|
4
4
|
"""
|
5
5
|
|
6
6
|
alias Kanta.Translations.{
|
7
|
+ ApplicationSources,
|
7
8
|
Contexts,
|
8
9
|
Domains,
|
9
10
|
Locales,
|
|
@@ -12,34 +13,49 @@ defmodule Kanta.Translations do
|
12
13
|
SingularTranslations
|
13
14
|
}
|
14
15
|
|
16
|
+ # APPLICATION SOURCES
|
17
|
+ defdelegate list_application_sources(params \\ []), to: ApplicationSources
|
18
|
+ defdelegate get_application_source(params), to: ApplicationSources
|
19
|
+ defdelegate create_application_source(attrs, opts \\ []), to: ApplicationSources
|
20
|
+ defdelegate change_application_source(attrs, params \\ %{}), to: ApplicationSources
|
21
|
+ defdelegate application_sources_empty?(), to: ApplicationSources
|
22
|
+
|
23
|
+ defdelegate update_application_source(application_source, attrs, opts \\ []),
|
24
|
+ to: ApplicationSources
|
25
|
+
|
15
26
|
# CONTEXTS
|
16
27
|
defdelegate list_contexts(params \\ []), to: Contexts
|
28
|
+ defdelegate list_all_contexts(params \\ []), to: Contexts
|
17
29
|
defdelegate get_context(params), to: Contexts
|
18
|
- defdelegate create_context(params), to: Contexts
|
30
|
+ defdelegate create_context(params, opts \\ []), to: Contexts
|
19
31
|
|
20
32
|
# DOMAINS
|
21
33
|
defdelegate list_domains(params \\ []), to: Domains
|
34
|
+ defdelegate list_all_domains(params \\ []), to: Domains
|
22
35
|
defdelegate get_domain(params \\ []), to: Domains
|
23
|
- defdelegate create_domain(attrs), to: Domains
|
36
|
+ defdelegate create_domain(attrs, opts \\ []), to: Domains
|
24
37
|
|
25
38
|
# MESSAGES
|
26
39
|
defdelegate list_messages(params \\ []), to: Messages
|
40
|
+ defdelegate list_all_messages(params \\ []), to: Messages
|
27
41
|
defdelegate get_message(params \\ []), to: Messages
|
28
42
|
defdelegate get_messages_count(), to: Messages
|
29
|
- defdelegate create_message(attrs), to: Messages
|
43
|
+ defdelegate create_message(attrs, opts \\ []), to: Messages
|
30
44
|
|
31
45
|
# LOCALES
|
32
46
|
defdelegate list_locales(params \\ []), to: Locales
|
33
47
|
defdelegate get_locale(params \\ []), to: Locales
|
34
|
- defdelegate update_locale(locale, attrs), to: Locales
|
48
|
+ defdelegate update_locale(locale, attrs, opts \\ []), to: Locales
|
35
49
|
|
36
50
|
# TRANSLATIONS
|
37
51
|
defdelegate list_plural_translations(params), to: PluralTranslations
|
38
52
|
defdelegate get_plural_translation(params), to: PluralTranslations
|
39
|
- defdelegate create_plural_translation(attrs), to: PluralTranslations
|
40
|
- defdelegate update_plural_translation(translation, attrs), to: PluralTranslations
|
53
|
+ defdelegate create_plural_translation(attrs, opts \\ []), to: PluralTranslations
|
54
|
+ defdelegate update_plural_translation(translation, attrs, opts \\ []), to: PluralTranslations
|
41
55
|
|
42
56
|
defdelegate get_singular_translation(params), to: SingularTranslations
|
43
|
- defdelegate create_singular_translation(attrs), to: SingularTranslations
|
44
|
- defdelegate update_singular_translation(translation, attrs), to: SingularTranslations
|
57
|
+ defdelegate create_singular_translation(attrs, opts \\ []), to: SingularTranslations
|
58
|
+
|
59
|
+ defdelegate update_singular_translation(translation, attrs, opts \\ []),
|
60
|
+ to: SingularTranslations
|
45
61
|
end
|
added
lib/kanta/translations/application_source.ex
|
@@ -0,0 +1,34 @@
|
1
|
+ defmodule Kanta.Translations.ApplicationSource do
|
2
|
+ @moduledoc """
|
3
|
+ Application source DB model used when dealing with multiple apps, for example mobile app
|
4
|
+ """
|
5
|
+
|
6
|
+ use Ecto.Schema
|
7
|
+ import Ecto.Changeset
|
8
|
+
|
9
|
+ alias Kanta.Translations.Message
|
10
|
+
|
11
|
+ @required_fields ~w(name)a
|
12
|
+ @optional_fields ~w(description color)a
|
13
|
+
|
14
|
+ @type t() :: Kanta.Translations.ApplicationSourceSpec.t()
|
15
|
+
|
16
|
+ @derive {Jason.Encoder, only: [:id] ++ @required_fields ++ @optional_fields}
|
17
|
+
|
18
|
+ schema "kanta_application_sources" do
|
19
|
+ field :name, :string
|
20
|
+ field :description, :string
|
21
|
+ field :color, :string
|
22
|
+
|
23
|
+ has_many :messages, Message
|
24
|
+
|
25
|
+ timestamps()
|
26
|
+ end
|
27
|
+
|
28
|
+ def changeset(struct, params) do
|
29
|
+ struct
|
30
|
+ |> cast(params, @required_fields ++ @optional_fields)
|
31
|
+ |> validate_required(@required_fields)
|
32
|
+ |> unique_constraint([:name])
|
33
|
+ end
|
34
|
+ end
|
added
lib/kanta/translations/application_source/application_source_spec.ex
|
@@ -0,0 +1,18 @@
|
1
|
+ defmodule Kanta.Translations.ApplicationSourceSpec do
|
2
|
+ @moduledoc """
|
3
|
+ Includes type specs for application source.
|
4
|
+ """
|
5
|
+
|
6
|
+ alias Kanta.Translations.{ApplicationSource, Message}
|
7
|
+ alias Kanta.Types
|
8
|
+
|
9
|
+ @type t() :: %ApplicationSource{
|
10
|
+ id: Types.field(Types.id()),
|
11
|
+ name: Types.field(String.t()),
|
12
|
+ description: Types.field(String.t()),
|
13
|
+ color: Types.field(String.t()),
|
14
|
+ messages: [Message.t()],
|
15
|
+ inserted_at: Types.field(NaiveDateTime.t()),
|
16
|
+ updated_at: Types.field(NaiveDateTime.t())
|
17
|
+ }
|
18
|
+ end
|
added
lib/kanta/translations/application_source/application_sources.ex
|
@@ -0,0 +1,43 @@
|
1
|
+ defmodule Kanta.Translations.ApplicationSources do
|
2
|
+ @moduledoc """
|
3
|
+ ApplicationSources Kanta subcontext
|
4
|
+ """
|
5
|
+
|
6
|
+ alias Kanta.Translations.ApplicationSource
|
7
|
+
|
8
|
+ alias Kanta.Translations.ApplicationSources.Finders.{
|
9
|
+ GetApplicationSource,
|
10
|
+ ListApplicationSources
|
11
|
+ }
|
12
|
+
|
13
|
+ def list_application_sources(params \\ []) do
|
14
|
+ ListApplicationSources.find(params)
|
15
|
+ end
|
16
|
+
|
17
|
+ def get_application_source(params) do
|
18
|
+ GetApplicationSource.find(params)
|
19
|
+ end
|
20
|
+
|
21
|
+ def create_application_source(attrs, opts \\ []) do
|
22
|
+ %ApplicationSource{}
|
23
|
+ |> ApplicationSource.changeset(attrs)
|
24
|
+ |> Kanta.Repo.get_repo().insert(opts)
|
25
|
+ end
|
26
|
+
|
27
|
+ def update_application_source(%ApplicationSource{} = application_source, attrs, opts \\ []) do
|
28
|
+ application_source
|
29
|
+ |> ApplicationSource.changeset(attrs)
|
30
|
+ |> Kanta.Repo.get_repo().update(opts)
|
31
|
+ end
|
32
|
+
|
33
|
+ def change_application_source(%ApplicationSource{} = application_source, params \\ %{}) do
|
34
|
+ ApplicationSource.changeset(application_source, params)
|
35
|
+ end
|
36
|
+
|
37
|
+ def application_sources_empty? do
|
38
|
+ %{entries: application_sources, metadata: _application_sources_metadata} =
|
39
|
+ list_application_sources(page: 1, per_page: 1)
|
40
|
+
|
41
|
+ Enum.empty?(application_sources)
|
42
|
+ end
|
43
|
+ end
|
added
lib/kanta/translations/application_source/finders/get_application_source.ex
|
@@ -0,0 +1,47 @@
|
1
|
+ defmodule Kanta.Translations.ApplicationSources.Finders.GetApplicationSource do
|
2
|
+ @moduledoc """
|
3
|
+ Query module aka Finder responsible for finding application source
|
4
|
+ """
|
5
|
+
|
6
|
+ use Kanta.Query,
|
7
|
+ module: Kanta.Translations.ApplicationSource,
|
8
|
+ binding: :domain
|
9
|
+
|
10
|
+ alias Kanta.Cache
|
11
|
+ alias Kanta.Translations.ApplicationSource
|
12
|
+
|
13
|
+ def find(params \\ []) do
|
14
|
+ cache_key = Cache.generate_cache_key("application_source", params)
|
15
|
+
|
16
|
+ with {:error, _, :not_cached} <- find_in_cache(cache_key),
|
17
|
+ {:ok, %ApplicationSource{} = application_source} <- find_in_database(params) do
|
18
|
+ Cache.put(cache_key, application_source)
|
19
|
+
|
20
|
+ {:ok, application_source}
|
21
|
+ else
|
22
|
+ {:ok, %ApplicationSource{} = application_source} -> {:ok, application_source}
|
23
|
+ {:error, _, :not_found} -> {:error, :application_source, :not_found}
|
24
|
+ end
|
25
|
+ end
|
26
|
+
|
27
|
+ defp find_in_cache(cache_key) do
|
28
|
+ case Cache.get(cache_key) do
|
29
|
+ nil ->
|
30
|
+ {:error, :application_source, :not_cached}
|
31
|
+
|
32
|
+ cached_application_source ->
|
33
|
+ {:ok, cached_application_source}
|
34
|
+ end
|
35
|
+ end
|
36
|
+
|
37
|
+ defp find_in_database(params) do
|
38
|
+ base()
|
39
|
+ |> filter_query(params[:filter])
|
40
|
+ |> preload_resources(params[:preloads] || [])
|
41
|
+ |> one()
|
42
|
+ |> case do
|
43
|
+ %ApplicationSource{} = application_source -> {:ok, application_source}
|
44
|
+ _ -> {:error, :application_source, :not_found}
|
45
|
+ end
|
46
|
+ end
|
47
|
+ end
|
added
lib/kanta/translations/application_source/finders/list_application_sources.ex
|
@@ -0,0 +1,16 @@
|
1
|
+ defmodule Kanta.Translations.ApplicationSources.Finders.ListApplicationSources do
|
2
|
+ @moduledoc """
|
3
|
+ Query module aka Finder responsible for listing application sources
|
4
|
+ """
|
5
|
+
|
6
|
+ use Kanta.Query,
|
7
|
+ module: Kanta.Translations.ApplicationSource,
|
8
|
+ binding: :domain
|
9
|
+
|
10
|
+ def find(params \\ []) do
|
11
|
+ base()
|
12
|
+ |> filter_query(params[:filter])
|
13
|
+ |> preload_resources(params[:preloads] || [])
|
14
|
+ |> paginate(params[:page], params[:per_page])
|
15
|
+ end
|
16
|
+ end
|
changed
lib/kanta/translations/context.ex
|
@@ -3,7 +3,7 @@ defmodule Kanta.Translations.Context do
|
3
3
|
Gettext Context DB model
|
4
4
|
"""
|
5
5
|
|
6
|
- use Ecto.Schema
|
6
|
+ use Kanta.Schema
|
7
7
|
import Ecto.Changeset
|
8
8
|
|
9
9
|
alias Kanta.Translations.Message
|
changed
lib/kanta/translations/context/contexts.ex
|
@@ -4,19 +4,23 @@ defmodule Kanta.Translations.Contexts do
|
4
4
|
"""
|
5
5
|
|
6
6
|
alias Kanta.Translations.Context
|
7
|
- alias Kanta.Translations.Contexts.Finders.{GetContext, ListContexts}
|
7
|
+ alias Kanta.Translations.Contexts.Finders.{GetContext, ListAllContexts, ListContexts}
|
8
8
|
|
9
9
|
def list_contexts(params \\ []) do
|
10
10
|
ListContexts.find(params)
|
11
11
|
end
|
12
12
|
|
13
|
+ def list_all_contexts(params \\ []) do
|
14
|
+ ListAllContexts.find(params)
|
15
|
+ end
|
16
|
+
|
13
17
|
def get_context(params) do
|
14
18
|
GetContext.find(params)
|
15
19
|
end
|
16
20
|
|
17
|
- def create_context(attrs) do
|
21
|
+ def create_context(attrs, opts \\ []) do
|
18
22
|
%Context{}
|
19
23
|
|> Context.changeset(attrs)
|
20
|
- |> Kanta.Repo.get_repo().insert()
|
24
|
+ |> Kanta.Repo.get_repo().insert(opts)
|
21
25
|
end
|
22
26
|
end
|
added
lib/kanta/translations/context/finders/list_all_contexts.ex
|
@@ -0,0 +1,18 @@
|
1
|
+ defmodule Kanta.Translations.Contexts.Finders.ListAllContexts do
|
2
|
+ @moduledoc """
|
3
|
+ Query module aka Finder responsible for listing all gettext contexts
|
4
|
+ """
|
5
|
+
|
6
|
+ use Kanta.Query,
|
7
|
+ module: Kanta.Translations.Context,
|
8
|
+ binding: :context
|
9
|
+
|
10
|
+ def find(params \\ []) do
|
11
|
+ repo = Kanta.Repo.get_repo()
|
12
|
+
|
13
|
+ base()
|
14
|
+ |> filter_query(params[:filter])
|
15
|
+ |> preload_resources(params[:preloads] || [])
|
16
|
+ |> repo.all()
|
17
|
+ end
|
18
|
+ end
|
changed
lib/kanta/translations/domain.ex
|
@@ -3,7 +3,7 @@ defmodule Kanta.Translations.Domain do
|
3
3
|
Gettext domain DB model
|
4
4
|
"""
|
5
5
|
|
6
|
- use Ecto.Schema
|
6
|
+ use Kanta.Schema
|
7
7
|
import Ecto.Changeset
|
8
8
|
|
9
9
|
alias Kanta.Translations.Message
|
changed
lib/kanta/translations/domain/domains.ex
|
@@ -6,19 +6,23 @@ defmodule Kanta.Translations.Domains do
|
6
6
|
alias Kanta.Repo
|
7
7
|
|
8
8
|
alias Kanta.Translations.Domain
|
9
|
- alias Kanta.Translations.Domains.Finders.{GetDomain, ListDomains}
|
9
|
+ alias Kanta.Translations.Domains.Finders.{GetDomain, ListAllDomains, ListDomains}
|
10
10
|
|
11
11
|
def list_domains(params \\ []) do
|
12
12
|
ListDomains.find(params)
|
13
13
|
end
|
14
14
|
|
15
|
+ def list_all_domains(params \\ []) do
|
16
|
+ ListAllDomains.find(params)
|
17
|
+ end
|
18
|
+
|
15
19
|
def get_domain(params) do
|
16
20
|
GetDomain.find(params)
|
17
21
|
end
|
18
22
|
|
19
|
- def create_domain(attrs) do
|
23
|
+ def create_domain(attrs, opts \\ []) do
|
20
24
|
%Domain{}
|
21
25
|
|> Domain.changeset(attrs)
|
22
|
- |> Repo.get_repo().insert()
|
26
|
+ |> Repo.get_repo().insert(opts)
|
23
27
|
end
|
24
28
|
end
|
added
lib/kanta/translations/domain/finders/list_all_domains.ex
|
@@ -0,0 +1,18 @@
|
1
|
+ defmodule Kanta.Translations.Domains.Finders.ListAllDomains do
|
2
|
+ @moduledoc """
|
3
|
+ Query module aka Finder responsible for listing all gettext domains
|
4
|
+ """
|
5
|
+
|
6
|
+ use Kanta.Query,
|
7
|
+ module: Kanta.Translations.Domain,
|
8
|
+ binding: :domain
|
9
|
+
|
10
|
+ def find(params \\ []) do
|
11
|
+ repo = Kanta.Repo.get_repo()
|
12
|
+
|
13
|
+ base()
|
14
|
+ |> filter_query(params[:filter])
|
15
|
+ |> preload_resources(params[:preloads] || [])
|
16
|
+ |> repo.all()
|
17
|
+ end
|
18
|
+ end
|
changed
lib/kanta/translations/locale.ex
|
@@ -3,7 +3,7 @@ defmodule Kanta.Translations.Locale do
|
3
3
|
Locale DB model
|
4
4
|
"""
|
5
5
|
|
6
|
- use Ecto.Schema
|
6
|
+ use Kanta.Schema
|
7
7
|
import Ecto.Changeset
|
8
8
|
alias Kanta.Translations.SingularTranslation
|
changed
lib/kanta/translations/locale/locales.ex
|
@@ -16,8 +16,8 @@ defmodule Kanta.Translations.Locales do
|
16
16
|
GetLocale.find(params)
|
17
17
|
end
|
18
18
|
|
19
|
- def update_locale(locale, attrs \\ %{}) do
|
19
|
+ def update_locale(locale, attrs \\ %{}, opts \\ []) do
|
20
20
|
Locale.changeset(locale, attrs)
|
21
|
- |> Repo.get_repo().update()
|
21
|
+ |> Repo.get_repo().update(opts)
|
22
22
|
end
|
23
23
|
end
|
changed
lib/kanta/translations/message.ex
|
@@ -3,17 +3,24 @@ defmodule Kanta.Translations.Message do
|
3
3
|
Gettext message DB model
|
4
4
|
"""
|
5
5
|
|
6
|
- use Ecto.Schema
|
6
|
+ use Kanta.Schema
|
7
7
|
import Ecto.Changeset
|
8
8
|
|
9
|
- alias Kanta.Translations.{Context, Domain, PluralTranslation, SingularTranslation}
|
9
|
+ alias Kanta.Translations.{
|
10
|
+ ApplicationSource,
|
11
|
+ Context,
|
12
|
+ Domain,
|
13
|
+ PluralTranslation,
|
14
|
+ SingularTranslation
|
15
|
+ }
|
10
16
|
|
11
17
|
@required_fields ~w(msgid message_type)a
|
12
|
- @optional_fields ~w(domain_id context_id)a
|
18
|
+ @optional_fields ~w(domain_id context_id application_source_id)a
|
19
|
+ @relations ~w(domain context singular_translations plural_translations)a
|
13
20
|
|
14
21
|
@type t() :: Kanta.Translations.MessageSpec.t()
|
15
22
|
|
16
|
- @derive {Jason.Encoder, only: [:id] ++ @required_fields ++ @optional_fields}
|
23
|
+ @derive {Jason.Encoder, only: [:id] ++ @required_fields ++ @optional_fields ++ @relations}
|
17
24
|
|
18
25
|
schema "kanta_messages" do
|
19
26
|
field :msgid, :string
|
|
@@ -21,6 +28,7 @@ defmodule Kanta.Translations.Message do
|
21
28
|
|
22
29
|
belongs_to :domain, Domain
|
23
30
|
belongs_to :context, Context
|
31
|
+ belongs_to :application_source, ApplicationSource
|
24
32
|
|
25
33
|
has_many :singular_translations, SingularTranslation
|
26
34
|
has_many :plural_translations, PluralTranslation
|
changed
lib/kanta/translations/messages/finders/get_message.ex
|
@@ -46,10 +46,15 @@ defmodule Kanta.Translations.Messages.Finders.GetMessage do
|
46
46
|
%Message{} = message -> {:ok, message}
|
47
47
|
_ -> {:error, :message, :not_found}
|
48
48
|
end
|
49
|
- |> database_fallback_public_prefix(params, opts)
|
49
|
+ |> database_fallback_public_prefix(params, opts, Repo.get_repo().__adapter__())
|
50
50
|
end
|
51
51
|
|
52
|
- defp database_fallback_public_prefix({:error, :message, :not_found} = result, params, repo_opts) do
|
52
|
+ defp database_fallback_public_prefix(
|
53
|
+ {:error, :message, :not_found} = result,
|
54
|
+ params,
|
55
|
+ repo_opts,
|
56
|
+ Ecto.Adapters.Postgres
|
57
|
+ ) do
|
53
58
|
if public_prefix?(repo_opts) do
|
54
59
|
result
|
55
60
|
else
|
|
@@ -58,7 +63,7 @@ defmodule Kanta.Translations.Messages.Finders.GetMessage do
|
58
63
|
end
|
59
64
|
end
|
60
65
|
|
61
|
- defp database_fallback_public_prefix(result, _, _), do: result
|
66
|
+ defp database_fallback_public_prefix(result, _, _, _), do: result
|
62
67
|
|
63
68
|
defp public_prefix?(repo_opts) do
|
64
69
|
config_prefix = Repo.get_repo().default_options(:all) |> Keyword.get(:prefix, :unset)
|
added
lib/kanta/translations/messages/finders/list_all_messages.ex
|
@@ -0,0 +1,22 @@
|
1
|
+ defmodule Kanta.Translations.Messages.Finders.ListAllMessages do
|
2
|
+ @moduledoc """
|
3
|
+ Query module aka Finder responsible for listing all gettext messages
|
4
|
+ """
|
5
|
+
|
6
|
+ use Kanta.Query,
|
7
|
+ module: Kanta.Translations.Message,
|
8
|
+ binding: :message
|
9
|
+
|
10
|
+ @available_filters ~w(domain_id context_id application_source_id)
|
11
|
+
|
12
|
+ def find(params \\ []) do
|
13
|
+ repo = Kanta.Repo.get_repo()
|
14
|
+ filters = params[:filter] || %{}
|
15
|
+ query_filters = Map.take(filters, @available_filters)
|
16
|
+
|
17
|
+ base()
|
18
|
+ |> filter_query(query_filters)
|
19
|
+ |> preload_resources(params[:preloads] || [])
|
20
|
+ |> repo.all()
|
21
|
+ end
|
22
|
+ end
|
changed
lib/kanta/translations/messages/finders/list_messages.ex
|
@@ -10,7 +10,7 @@ defmodule Kanta.Translations.Messages.Finders.ListMessages do
|
10
10
|
alias Kanta.Translations.PluralTranslations.Finders.ListPluralTranslations
|
11
11
|
alias Kanta.Translations.SingularTranslations.Finders.ListSingularTranslations
|
12
12
|
|
13
|
- @available_filters ~w(domain_id context_id)
|
13
|
+ @available_filters ~w(domain_id context_id application_source_id)
|
14
14
|
|
15
15
|
def find(params \\ []) do
|
16
16
|
filters = params[:filter] || %{}
|
changed
lib/kanta/translations/messages/message_spec.ex
|
@@ -3,13 +3,23 @@ defmodule Kanta.Translations.MessageSpec do
|
3
3
|
Includes type specs for message.
|
4
4
|
"""
|
5
5
|
|
6
|
- alias Kanta.Translations.{Context, Domain, Message, PluralTranslation, SingularTranslation}
|
6
|
+ alias Kanta.Translations.{
|
7
|
+ ApplicationSource,
|
8
|
+ Context,
|
9
|
+ Domain,
|
10
|
+ Message,
|
11
|
+ PluralTranslation,
|
12
|
+ SingularTranslation
|
13
|
+ }
|
14
|
+
|
7
15
|
alias Kanta.Types
|
8
16
|
|
9
17
|
@type t() :: %Message{
|
10
18
|
id: Types.field(Types.id()),
|
11
19
|
msgid: Types.field(String.t()),
|
12
20
|
message_type: :singular | :plural,
|
21
|
+ application_source: Types.field(ApplicationSource.t()),
|
22
|
+ application_source_id: Types.field(Types.id()),
|
13
23
|
domain: Types.field(Domain.t()),
|
14
24
|
domain_id: Types.field(Types.id()),
|
15
25
|
context: Types.field(Context.t()),
|
changed
lib/kanta/translations/messages/messages.ex
|
@@ -7,12 +7,16 @@ defmodule Kanta.Translations.Messages do
|
7
7
|
|
8
8
|
alias Kanta.Translations.Message
|
9
9
|
|
10
|
- alias Kanta.Translations.Messages.Finders.{GetMessage, ListMessages}
|
10
|
+ alias Kanta.Translations.Messages.Finders.{GetMessage, ListAllMessages, ListMessages}
|
11
11
|
|
12
12
|
def list_messages(params \\ []) do
|
13
13
|
ListMessages.find(params)
|
14
14
|
end
|
15
15
|
|
16
|
+ def list_all_messages(params \\ []) do
|
17
|
+ ListAllMessages.find(params)
|
18
|
+ end
|
19
|
+
|
16
20
|
def get_message(params \\ []) do
|
17
21
|
GetMessage.find(params)
|
18
22
|
end
|
|
@@ -21,8 +25,8 @@ defmodule Kanta.Translations.Messages do
|
21
25
|
Repo.get_repo().aggregate(Message, :count)
|
22
26
|
end
|
23
27
|
|
24
|
- def create_message(attrs) do
|
25
|
- %Message{} |> Message.changeset(attrs) |> Repo.get_repo().insert()
|
28
|
+ def create_message(attrs, opts \\ []) do
|
29
|
+ %Message{} |> Message.changeset(attrs) |> Repo.get_repo().insert(opts)
|
26
30
|
end
|
27
31
|
|
28
32
|
def update_message(message, attrs) do
|
changed
lib/kanta/translations/plural_translation.ex
|
@@ -3,7 +3,7 @@ defmodule Kanta.Translations.PluralTranslation do
|
3
3
|
Plural translation DB model
|
4
4
|
"""
|
5
5
|
|
6
|
- use Ecto.Schema
|
6
|
+ use Kanta.Schema
|
7
7
|
import Ecto.Changeset
|
8
8
|
|
9
9
|
alias Kanta.Translations.{Locale, Message}
|
changed
lib/kanta/translations/plural_translation/plural_translations.ex
|
@@ -20,10 +20,10 @@ defmodule Kanta.Translations.PluralTranslations do
|
20
20
|
GetPluralTranslation.find(params)
|
21
21
|
end
|
22
22
|
|
23
|
- def create_plural_translation(attrs) do
|
23
|
+ def create_plural_translation(attrs, opts \\ []) do
|
24
24
|
attrs
|
25
25
|
|> then(&PluralTranslation.changeset(%PluralTranslation{}, &1))
|
26
|
- |> Repo.get_repo().insert()
|
26
|
+ |> Repo.get_repo().insert(opts)
|
27
27
|
|> case do
|
28
28
|
{:ok, plural_translation} ->
|
29
29
|
cache_key =
|
|
@@ -43,9 +43,9 @@ defmodule Kanta.Translations.PluralTranslations do
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
- def update_plural_translation(translation, attrs) do
|
46
|
+ def update_plural_translation(translation, attrs, opts \\ []) do
|
47
47
|
PluralTranslation.changeset(translation, attrs)
|
48
|
- |> Repo.get_repo().update()
|
48
|
+ |> Repo.get_repo().update(opts)
|
49
49
|
|> case do
|
50
50
|
{:ok, translation} ->
|
51
51
|
cache_key =
|
changed
lib/kanta/translations/singular_translation.ex
|
@@ -3,7 +3,7 @@ defmodule Kanta.Translations.SingularTranslation do
|
3
3
|
Singular translation DB model
|
4
4
|
"""
|
5
5
|
|
6
|
- use Ecto.Schema
|
6
|
+ use Kanta.Schema
|
7
7
|
import Ecto.Changeset
|
8
8
|
alias Kanta.Translations.{Locale, Message}
|
changed
lib/kanta/translations/singular_translation/singular_translations.ex
|
@@ -13,10 +13,10 @@ defmodule Kanta.Translations.SingularTranslations do
|
13
13
|
GetSingularTranslation.find(params)
|
14
14
|
end
|
15
15
|
|
16
|
- def create_singular_translation(attrs) do
|
16
|
+ def create_singular_translation(attrs, opts \\ []) do
|
17
17
|
attrs
|
18
18
|
|> then(&SingularTranslation.changeset(%SingularTranslation{}, &1))
|
19
|
- |> Repo.get_repo().insert()
|
19
|
+ |> Repo.get_repo().insert(opts)
|
20
20
|
|> case do
|
21
21
|
{:ok, singular_translation} ->
|
22
22
|
cache_key =
|
|
@@ -35,9 +35,9 @@ defmodule Kanta.Translations.SingularTranslations do
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
- def update_singular_translation(translation, attrs) do
|
38
|
+ def update_singular_translation(translation, attrs, opts \\ []) do
|
39
39
|
SingularTranslation.changeset(translation, attrs)
|
40
|
- |> Repo.get_repo().update()
|
40
|
+ |> Repo.get_repo().update(opts)
|
41
41
|
|> case do
|
42
42
|
{:ok, translation} ->
|
43
43
|
cache_key =
|
added
lib/kanta/utils/colors.ex
|
@@ -0,0 +1,10 @@
|
1
|
+ defmodule Kanta.Utils.Colors do
|
2
|
+ @moduledoc """
|
3
|
+ Color definitions
|
4
|
+ """
|
5
|
+
|
6
|
+ @default_color "#7E37D8"
|
7
|
+
|
8
|
+ @spec default_color() :: binary()
|
9
|
+ def default_color(), do: @default_color
|
10
|
+ end
|
added
lib/kanta/utils/compilation.ex
|
@@ -0,0 +1,14 @@
|
1
|
+ defmodule Kanta.Utils.Compilation do
|
2
|
+ @moduledoc false
|
3
|
+
|
4
|
+ @doc """
|
5
|
+ Returns `true` if it is run during compilation.
|
6
|
+
|
7
|
+ This function is used to handle translation messages during compilation time in macros and module attributes.
|
8
|
+ """
|
9
|
+ @spec compiling?() :: boolean()
|
10
|
+ def compiling? do
|
11
|
+ Code.ensure_loaded?(Code) &&
|
12
|
+ Code.can_await_module_compilation?()
|
13
|
+ end
|
14
|
+ end
|
changed
lib/kanta/utils/database_populator.ex
|
@@ -39,5 +39,8 @@ defmodule Kanta.Utils.DatabasePopulator do
|
39
39
|
defp reduce_keys_to_atoms({key, val}) when is_map(val),
|
40
40
|
do: {String.to_existing_atom(key), keys_to_atoms(val)}
|
41
41
|
|
42
|
+ defp reduce_keys_to_atoms({key, val}) when is_list(val),
|
43
|
+ do: {String.to_existing_atom(key), Enum.map(val, &keys_to_atoms/1)}
|
44
|
+
|
42
45
|
defp reduce_keys_to_atoms({key, val}), do: {String.to_existing_atom(key), val}
|
43
46
|
end
|
changed
lib/kanta/utils/get_schemata.ex
|
@@ -4,6 +4,7 @@ defmodule Kanta.Utils.GetSchemata do
|
4
4
|
alias Kanta.Specs.SchemataSpec
|
5
5
|
|
6
6
|
alias Kanta.Translations.{
|
7
|
+ ApplicationSource,
|
7
8
|
Context,
|
8
9
|
Domain,
|
9
10
|
Locale,
|
|
@@ -13,6 +14,7 @@ defmodule Kanta.Utils.GetSchemata do
|
13
14
|
}
|
14
15
|
|
15
16
|
@schemata [
|
17
|
+ {"application_sources", %{schema: ApplicationSource, conflict_target: [:name]}},
|
16
18
|
{"contexts", %{schema: Context, conflict_target: [:name]}},
|
17
19
|
{"domains", %{schema: Domain, conflict_target: [:name]}},
|
18
20
|
{"locales", %{schema: Locale, conflict_target: [:iso639_code]}},
|
added
lib/kanta/utils/param_parsers.ex
|
@@ -0,0 +1,29 @@
|
1
|
+ defmodule Kanta.Utils.ParamParsers do
|
2
|
+ @moduledoc false
|
3
|
+
|
4
|
+ def default_id_parser(id) do
|
5
|
+ case Integer.parse(id) do
|
6
|
+ {id, _} -> {:ok, id}
|
7
|
+ _ -> :error
|
8
|
+ end
|
9
|
+ end
|
10
|
+
|
11
|
+ def parse_page(page) do
|
12
|
+ case Integer.parse(page) do
|
13
|
+ {page, _} -> page
|
14
|
+ _ -> 1
|
15
|
+ end
|
16
|
+ end
|
17
|
+
|
18
|
+ def parse_id_filter(id) do
|
19
|
+ run_parse_function(Kanta.config().id_parse_function, id)
|
20
|
+ end
|
21
|
+
|
22
|
+ defp run_parse_function(parse_function, id) when is_function(parse_function, 1) do
|
23
|
+ parse_function.(id)
|
24
|
+ end
|
25
|
+
|
26
|
+ defp run_parse_function({module, parse_function, 1}, id) do
|
27
|
+ apply(module, parse_function, [id])
|
28
|
+ end
|
29
|
+ end
|
changed
lib/kanta_web.ex
|
@@ -99,8 +99,9 @@ defmodule KantaWeb do
|
99
99
|
quote do
|
100
100
|
@endpoint Application.compile_env(:kanta, :endpoint)
|
101
101
|
|
102
|
- # Use all HTML functionality (forms, tags, etc)
|
103
|
- use Phoenix.HTML
|
102
|
+ import Phoenix.HTML
|
103
|
+ import Phoenix.HTML.Form
|
104
|
+ use PhoenixHTMLHelpers
|
104
105
|
|
105
106
|
# Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc)
|
106
107
|
import Phoenix.Component
|
|
@@ -110,6 +111,7 @@ defmodule KantaWeb do
|
110
111
|
|
111
112
|
import Kanta.Utils.ModuleUtils
|
112
113
|
|
114
|
+ alias KantaWeb.Components.Icons
|
113
115
|
alias KantaWeb.Router.Helpers, as: Routes
|
114
116
|
unquote(verified_routes())
|
115
117
|
end
|
|
@@ -121,6 +123,33 @@ defmodule KantaWeb do
|
121
123
|
endpoint: Application.compile_env(:kanta, :endpoint),
|
122
124
|
router: KantaWeb.Router,
|
123
125
|
statics: KantaWeb.static_paths()
|
126
|
+
|
127
|
+ def dashboard_path(%Phoenix.LiveView.Socket{} = socket),
|
128
|
+ do: socket.router.__kanta_dashboard_prefix__()
|
129
|
+
|
130
|
+ def dashboard_path(%Plug.Conn{} = conn),
|
131
|
+ do: conn.private.phoenix_router.__kanta_dashboard_prefix__()
|
132
|
+
|
133
|
+ def dashboard_path(%Phoenix.LiveView.Socket{} = socket, "/" <> path),
|
134
|
+ do: dashboard_path(socket, path)
|
135
|
+
|
136
|
+ def dashboard_path(%Phoenix.LiveView.Socket{} = socket, path) do
|
137
|
+ unverified_path(
|
138
|
+ socket,
|
139
|
+ Kanta.Router,
|
140
|
+ socket.router.__kanta_dashboard_prefix__() <> "/" <> path
|
141
|
+ )
|
142
|
+ end
|
143
|
+
|
144
|
+ def dashboard_path(%Plug.Conn{} = conn, "/" <> path), do: dashboard_path(conn, path)
|
145
|
+
|
146
|
+ def dashboard_path(%Plug.Conn{} = conn, path) do
|
147
|
+ unverified_path(
|
148
|
+ conn,
|
149
|
+ Kanta.Router,
|
150
|
+ conn.private.phoenix_router.__kanta_dashboard_prefix__() <> "/" <> path
|
151
|
+ )
|
152
|
+ end
|
124
153
|
end
|
125
154
|
end
|
changed
lib/kanta_web/components/shared/icons.ex
|
@@ -230,4 +230,56 @@ defmodule KantaWeb.Components.Icons do
|
230
230
|
</svg>
|
231
231
|
"""
|
232
232
|
end
|
233
|
+
|
234
|
+ def computer(assigns) do
|
235
|
+ attrs = assigns_to_attributes(assigns)
|
236
|
+ assigns = assign(assigns, :attrs, attrs)
|
237
|
+
|
238
|
+ ~H"""
|
239
|
+ <svg {@attrs}
|
240
|
+ xmlns="https://www.w3.org/2000/svg"
|
241
|
+ width="24"
|
242
|
+ height="24"
|
243
|
+ viewBox="0 0 24 24"
|
244
|
+ fill="none"
|
245
|
+ stroke="currentColor"
|
246
|
+ stroke-width="2"
|
247
|
+ stroke-linecap="round"
|
248
|
+ stroke-linejoin="round"
|
249
|
+ >
|
250
|
+ <path stroke-linecap="round" stroke-linejoin="round" d="M9 17.25v1.007a3 3 0 0 1-.879 2.122L7.5 21h9l-.621-.621A3 3 0 0 1 15 18.257V17.25m6-12V15a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 15V5.25m18 0A2.25 2.25 0 0 0 18.75 3H5.25A2.25 2.25 0 0 0 3 5.25m18 0V12a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 12V5.25"></path>
|
251
|
+ </svg>
|
252
|
+
|
253
|
+ """
|
254
|
+ end
|
255
|
+
|
256
|
+ def chevron_left(assigns) do
|
257
|
+ attrs = assigns_to_attributes(assigns)
|
258
|
+ assigns = assign(assigns, :attrs, attrs)
|
259
|
+
|
260
|
+ ~H"""
|
261
|
+ <svg {@attrs}
|
262
|
+ xmlns="https://www.w3.org/2000/svg"
|
263
|
+ viewBox="0 0 20 20"
|
264
|
+ fill="currentColor"
|
265
|
+ >
|
266
|
+ <path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" />
|
267
|
+ </svg>
|
268
|
+ """
|
269
|
+ end
|
270
|
+
|
271
|
+ def chevron_right(assigns) do
|
272
|
+ attrs = assigns_to_attributes(assigns)
|
273
|
+ assigns = assign(assigns, :attrs, attrs)
|
274
|
+
|
275
|
+ ~H"""
|
276
|
+ <svg {@attrs}
|
277
|
+ xmlns="https://www.w3.org/2000/svg"
|
278
|
+ viewBox="0 0 20 20"
|
279
|
+ fill="currentColor"
|
280
|
+ >
|
281
|
+ <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
282
|
+ </svg>
|
283
|
+ """
|
284
|
+ end
|
233
285
|
end
|
changed
lib/kanta_web/components/shared/select/select.ex
|
@@ -15,7 +15,8 @@ defmodule KantaWeb.Components.Shared.Select do
|
15
15
|
if is_nil(field) do
|
16
16
|
List.first(options)
|
17
17
|
else
|
18
|
- Enum.find(options, &(&1.value == value_to_integer(field.value))) || List.first(options)
|
18
|
+ Enum.find(options, &(parse_select_value(&1.value) == field.value)) ||
|
19
|
+ List.first(options)
|
19
20
|
end
|
20
21
|
else
|
21
22
|
assigns.selected_option
|
|
@@ -40,12 +41,7 @@ defmodule KantaWeb.Components.Shared.Select do
|
40
41
|
}
|
41
42
|
end
|
42
43
|
|
43
|
- defp value_to_integer(nil), do: nil
|
44
|
- defp value_to_integer(""), do: nil
|
45
|
-
|
46
|
- defp value_to_integer(value) do
|
47
|
- String.to_integer(value)
|
48
|
- rescue
|
49
|
- _ in ArgumentError -> nil
|
50
|
- end
|
44
|
+ defp parse_select_value(nil), do: nil
|
45
|
+ defp parse_select_value(""), do: nil
|
46
|
+ defp parse_select_value(value), do: to_string(value)
|
51
47
|
end
|
changed
lib/kanta_web/components/shared/select/select.html.heex
|
@@ -1,7 +1,7 @@
|
1
1
|
<div
|
2
2
|
id={"#{@id}-wrapper"}
|
3
3
|
phx-hook="Select"
|
4
|
- x-data={"{ open: false, idx: -1, selectedIdx: #{Enum.find_index(@options, & &1.value == @field.value) || 0}, max: #{length(@options) - 1} }"}
|
4
|
+ x-data={"{ open: false, idx: -1, selectedIdx: #{Enum.find_index(@options, & parse_select_value(&1.value) == @field.value) || 0}, max: #{length(@options) - 1} }"}
|
5
5
|
x-init={"() => { $watch('selectedIdx', val => $dispatch('selected-change', { selectedIdx: val, id: '##{@id}' }) ) }"}
|
6
6
|
x-on:reset="open = false"
|
7
7
|
>
|
changed
lib/kanta_web/components/shared/toggle/toggle.html.heex
|
@@ -2,7 +2,7 @@
|
2
2
|
id={"#{@id}-wrapper"}
|
3
3
|
class="flex items-center"
|
4
4
|
phx-hook="Toggle"
|
5
|
- x-data="{ on: false }"
|
5
|
+ x-data={"{ on: #{@default_value} }"}
|
6
6
|
x-init={"() => { $watch('on', val => $dispatch('toggle-change', { id: '##{@id}', state: val }) ) }"}
|
7
7
|
>
|
8
8
|
<%= hidden_input @form, @field.name %>
|
added
lib/kanta_web/controllers/api/application_sources_controller.ex
|
@@ -0,0 +1,23 @@
|
1
|
+ defmodule KantaWeb.Api.ApplicationSourcesController do
|
2
|
+ @moduledoc false
|
3
|
+ use KantaWeb, :controller
|
4
|
+
|
5
|
+ alias Kanta.Translations
|
6
|
+ alias Kanta.Utils.DatabasePopulator
|
7
|
+
|
8
|
+ def index(conn, params) do
|
9
|
+ page = params |> Map.get("page", "1") |> String.to_integer()
|
10
|
+
|
11
|
+ conn
|
12
|
+ |> put_status(200)
|
13
|
+ |> json(Translations.list_application_sources(page: page))
|
14
|
+ end
|
15
|
+
|
16
|
+ def update(conn, %{"entries" => entries}) do
|
17
|
+ DatabasePopulator.call("application_sources", entries)
|
18
|
+
|
19
|
+ conn
|
20
|
+ |> put_status(200)
|
21
|
+ |> json(%{status: "OK"})
|
22
|
+ end
|
23
|
+ end
|
changed
lib/kanta_web/live/dashboard/dashboard_live/dashboard_live.html.heex
|
@@ -47,6 +47,9 @@
|
47
47
|
<%= if plugin_name |> Module.concat(DashboardComponent) |> module_exists?() do %>
|
48
48
|
<.live_component module={Module.concat(plugin_name, DashboardComponent)} id={plugin_name} />
|
49
49
|
<% end %>
|
50
|
+ <%= if plugin_name |> Module.concat(DashboardLive) |> module_exists?() do %>
|
51
|
+ <%= live_render(@socket, Module.concat(plugin_name, DashboardLive), id: plugin_name) %>
|
52
|
+ <% end %>
|
50
53
|
<% end %>
|
51
54
|
</div>
|
52
55
|
<% end %>
|
added
lib/kanta_web/live/translations/application_source_form_live/application_source_form_live.ex
|
@@ -0,0 +1,85 @@
|
1
|
+ defmodule KantaWeb.Translations.ApplicationSourceFormLive do
|
2
|
+ use KantaWeb, :live_view
|
3
|
+
|
4
|
+ alias Kanta.Translations
|
5
|
+ alias Kanta.Translations.ApplicationSource
|
6
|
+
|
7
|
+ def mount(%{"id" => application_source_id}, _session, socket) do
|
8
|
+ socket =
|
9
|
+ case get_application_source(application_source_id) do
|
10
|
+ nil ->
|
11
|
+ redirect(socket, to: dashboard_path(socket, "/application_sources"))
|
12
|
+
|
13
|
+ application_source ->
|
14
|
+ form = Translations.change_application_source(application_source)
|
15
|
+
|
16
|
+ socket
|
17
|
+ |> assign(:form, to_form(form))
|
18
|
+ |> assign(:application_source, application_source)
|
19
|
+ end
|
20
|
+
|
21
|
+ {:ok, socket}
|
22
|
+ end
|
23
|
+
|
24
|
+ def mount(_params, _session, socket) do
|
25
|
+ form = Translations.change_application_source(%ApplicationSource{})
|
26
|
+ socket = assign(socket, :form, to_form(form))
|
27
|
+
|
28
|
+ {:ok, socket}
|
29
|
+ end
|
30
|
+
|
31
|
+ def handle_event("validate", %{"application_source" => attrs}, socket) do
|
32
|
+ {action, application_source} =
|
33
|
+ if Map.has_key?(socket.assigns, :application_source) do
|
34
|
+ {:update, socket.assigns.application_source}
|
35
|
+ else
|
36
|
+ {:insert, %ApplicationSource{}}
|
37
|
+ end
|
38
|
+
|
39
|
+ form =
|
40
|
+ application_source
|
41
|
+ |> Translations.change_application_source(attrs)
|
42
|
+ |> Map.put(:action, action)
|
43
|
+
|
44
|
+ socket = assign(socket, form: to_form(form))
|
45
|
+
|
46
|
+ {:noreply, socket}
|
47
|
+ end
|
48
|
+
|
49
|
+ def handle_event(
|
50
|
+ "submit",
|
51
|
+ %{"application_source" => attrs},
|
52
|
+ %{assigns: %{application_source: application_source}} = socket
|
53
|
+ ) do
|
54
|
+ socket =
|
55
|
+ case Translations.update_application_source(application_source, attrs) do
|
56
|
+ {:ok, _application_source} ->
|
57
|
+ push_redirect(socket, to: dashboard_path(socket, "/application_sources"))
|
58
|
+
|
59
|
+ {:error, changeset} ->
|
60
|
+ assign(socket, :form, to_form(changeset))
|
61
|
+ end
|
62
|
+
|
63
|
+ {:noreply, socket}
|
64
|
+ end
|
65
|
+
|
66
|
+ def handle_event("submit", %{"application_source" => attrs}, socket) do
|
67
|
+ socket =
|
68
|
+ case Translations.create_application_source(attrs) do
|
69
|
+ {:ok, _application_source} ->
|
70
|
+ push_redirect(socket, to: dashboard_path(socket, "/application_sources"))
|
71
|
+
|
72
|
+ {:error, changeset} ->
|
73
|
+ assign(socket, :form, to_form(changeset))
|
74
|
+ end
|
75
|
+
|
76
|
+ {:noreply, socket}
|
77
|
+ end
|
78
|
+
|
79
|
+ defp get_application_source(id) do
|
80
|
+ case Translations.get_application_source(filter: [id: id]) do
|
81
|
+ {:ok, application_source} -> application_source
|
82
|
+ {:error, _, _reason} -> nil
|
83
|
+ end
|
84
|
+ end
|
85
|
+ end
|
added
lib/kanta_web/live/translations/application_source_form_live/application_source_form_live.html.heex
|
@@ -0,0 +1,71 @@
|
1
|
+ <div>
|
2
|
+ <div class="mb-6">
|
3
|
+ <div>
|
4
|
+ <div>
|
5
|
+ <nav class="sm:hidden" aria-label="Back">
|
6
|
+ <.link navigate={dashboard_path(@socket, "/application_sources")} class="flex items-center text-sm font-medium text-slate-400 hover:text-slate-200">
|
7
|
+ <Icons.chevron_left class="flex-shrink-0 -ml-1 mr-1 h-5 w-5 text-slate-500" aria-hidden="true" />
|
8
|
+ Back
|
9
|
+ </.link>
|
10
|
+ </nav>
|
11
|
+ <nav class="hidden sm:flex mb-2" aria-label="Breadcrumb">
|
12
|
+ <ol class="flex items-center space-x-4">
|
13
|
+ <li>
|
14
|
+ <div>
|
15
|
+ <.link patch={"/application_sources"} class="cursor-pointer text-sm font-medium text-slate-400 hover:text-slate-200">Application Sources</.link>
|
16
|
+ </div>
|
17
|
+ </li>
|
18
|
+ </ol>
|
19
|
+ </nav>
|
20
|
+ </div>
|
21
|
+ <div class="mt-2 md:flex md:items-center md:justify-between">
|
22
|
+ <div class="flex-1 min-w-0">
|
23
|
+ <h2 class="text-xl font-bold leading-7 text-slate-600 dark:text-content-light sm:text-2xl sm:truncate">
|
24
|
+ <%= if Map.has_key?(assigns, :application_source), do: "Updating application source", else: "Creating application source" %>
|
25
|
+ </h2>
|
26
|
+ </div>
|
27
|
+ </div>
|
28
|
+ </div>
|
29
|
+ </div>
|
30
|
+ <.form for={@form} phx-change="validate" phx-submit="submit" class="bg-white dark:bg-stone-900 shadow rounded-md mt-4 px-4 py-4 space-y-4">
|
31
|
+ <div class="pb-5 border-b border-slate-200 sm:flex sm:items-center sm:justify-between">
|
32
|
+ <h3 class="text-lg leading-6 font-medium text-primary-dark dark:text-accent-dark">
|
33
|
+ Application Source
|
34
|
+ </h3>
|
35
|
+ </div>
|
36
|
+ <div class="grid grid-cols-2 gap-4 lg:gap-8">
|
37
|
+ <div class="col-span-2 space-y-4">
|
38
|
+ <div>
|
39
|
+ <label for={@form[:name].id} class="block text-sm font-bold text-slate-700 dark:text-content-light">Name</label>
|
40
|
+ <input type="text" name={@form[:name].name} id={@form[:name].id} value={@form[:name].value} class="bg-slate-100 dark:bg-stone-700 shadow-sm focus:ring-primary focus:dark:ring-accent-dark focus:border-primary focus:dark:border-accent-dark block w-full sm:text-sm border-slate-300 rounded-md" />
|
41
|
+ <p :for={{error, _} <- @form[:name].errors} class="mt-3 text-sm leading-6 text-rose-600">
|
42
|
+ <%= error %>
|
43
|
+ </p>
|
44
|
+ </div>
|
45
|
+ <div>
|
46
|
+ <label for={@form[:description].id} class="block text-sm font-bold text-slate-700 dark:text-content-light">Description</label>
|
47
|
+ <div class="mt-1">
|
48
|
+ <textarea type="text" name={@form[:description].name} id={@form[:description].id} class="bg-slate-100 dark:bg-stone-700 shadow-sm focus:ring-primary focus:dark:ring-accent-dark focus:border-primary focus:dark:border-accent-dark block w-full sm:text-sm border-slate-300 rounded-md"><%= @form[:description].value %></textarea>
|
49
|
+ </div>
|
50
|
+ <p :for={{error, _} <- @form[:description].errors} class="mt-3 text-sm leading-6 text-rose-600">
|
51
|
+ <%= error %>
|
52
|
+ </p>
|
53
|
+ </div>
|
54
|
+ <div>
|
55
|
+ <label for={@form[:color].id} class="block text-sm font-bold text-slate-700 dark:text-content-light">Color</label>
|
56
|
+ <div class="mt-1">
|
57
|
+ <input type="color" name={@form[:color].name} id={@form[:color].id} value={@form[:color].value} class="bg-white dark:bg-base-dark text-content-dark dark:text-content-light shadow-sm focus:ring-primary focus:dark:ring-accent-dark focus:border-primary focus:dark:border-accent-dark block w-full sm:text-sm border-slate-300 rounded-md" />
|
58
|
+ </div>
|
59
|
+ <p :for={{error, _} <- @form[:color].errors} class="mt-3 text-sm leading-6 text-rose-600">
|
60
|
+ <%= error %>
|
61
|
+ </p>
|
62
|
+ </div>
|
63
|
+ </div>
|
64
|
+ <div class="col-span-2">
|
65
|
+ <button type="submit" class="w-full flex items-center justify-center px-4 py-4 mt-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-primary dark:bg-accent-dark hover:bg-primary-dark hover:dark:bg-accent-light focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-slate-800 focus:ring-primary focus:dark:ring-accent-dark">
|
66
|
+ Save
|
67
|
+ </button>
|
68
|
+ </div>
|
69
|
+ </div>
|
70
|
+ </.form>
|
71
|
+ </div>
|
added
lib/kanta_web/live/translations/application_sources_live/application_sources_live.ex
|
@@ -0,0 +1,36 @@
|
1
|
+ defmodule KantaWeb.Translations.ApplicationSourcesLive do
|
2
|
+ use KantaWeb, :live_view
|
3
|
+
|
4
|
+ alias Kanta.Translations
|
5
|
+ alias KantaWeb.Translations.ApplicationSourcesTable
|
6
|
+
|
7
|
+ alias KantaWeb.Components.Shared.Pagination
|
8
|
+
|
9
|
+ def mount(_params, _session, socket) do
|
10
|
+ %{entries: application_sources, metadata: application_sources_metadata} =
|
11
|
+ Translations.list_application_sources()
|
12
|
+
|
13
|
+ socket =
|
14
|
+ socket
|
15
|
+ |> assign(:application_sources, application_sources)
|
16
|
+ |> assign(:application_sources_metadata, application_sources_metadata)
|
17
|
+
|
18
|
+ {:ok, socket}
|
19
|
+ end
|
20
|
+
|
21
|
+ def handle_event("navigate", %{"to" => to}, socket) do
|
22
|
+ {:noreply, push_redirect(socket, to: "/kanta" <> to)}
|
23
|
+ end
|
24
|
+
|
25
|
+ def handle_event("page_changed", %{"index" => page_number}, socket) do
|
26
|
+ %{entries: application_sources, metadata: application_sources_metadata} =
|
27
|
+ Translations.list_application_sources(page: String.to_integer(page_number))
|
28
|
+
|
29
|
+ socket =
|
30
|
+ socket
|
31
|
+ |> assign(:application_sources, application_sources)
|
32
|
+ |> assign(:application_sources_metadata, application_sources_metadata)
|
33
|
+
|
34
|
+ {:noreply, socket}
|
35
|
+ end
|
36
|
+ end
|
added
lib/kanta_web/live/translations/application_sources_live/application_sources_live.html.heex
|
@@ -0,0 +1,18 @@
|
1
|
+ <div>
|
2
|
+ <div class="mt-2 md:flex md:items-center md:justify-between">
|
3
|
+ <div class="flex-1 min-w-0">
|
4
|
+ <h2 class="text-2xl font-bold leading-7 text-primary-dark dark:text-accent-dark sm:text-3xl sm:truncate">
|
5
|
+ Application Sources
|
6
|
+ </h2>
|
7
|
+ </div>
|
8
|
+ </div>
|
9
|
+ </div>
|
10
|
+ <div class="mt-4">
|
11
|
+ <div class="flex gap-2 justify-end">
|
12
|
+ <.link navigate={dashboard_path(@socket, "/application_sources/new")} class="font-semibold text-primary-dark dark:text-accent-dark">
|
13
|
+ Create application source
|
14
|
+ </.link>
|
15
|
+ </div>
|
16
|
+ <.live_component module={ApplicationSourcesTable} id="application_sources-table" application_sources={@application_sources} />
|
17
|
+ <Pagination.render metadata={@application_sources_metadata} on_page_change="page_changed" />
|
18
|
+ </div>
|
added
lib/kanta_web/live/translations/application_sources_live/components/application_sources_table/application_sources_table.ex
|
@@ -0,0 +1,15 @@
|
1
|
+ defmodule KantaWeb.Translations.ApplicationSourcesTable do
|
2
|
+ @moduledoc """
|
3
|
+ Application sources table component
|
4
|
+ """
|
5
|
+
|
6
|
+ use KantaWeb, :live_component
|
7
|
+
|
8
|
+ def update(socket, assigns) do
|
9
|
+ {:ok, assign(assigns, socket)}
|
10
|
+ end
|
11
|
+
|
12
|
+ def handle_event("edit_application_source", %{"id" => id}, socket) do
|
13
|
+ {:noreply, push_navigate(socket, to: dashboard_path(socket, "/application_sources/#{id}"))}
|
14
|
+ end
|
15
|
+ end
|
added
lib/kanta_web/live/translations/application_sources_live/components/application_sources_table/application_sources_table.html.heex
|
@@ -0,0 +1,48 @@
|
1
|
+ <div class="bg-base-light dark:bg-base-dark py-6">
|
2
|
+ <div class="w-full">
|
3
|
+ <div class="flex flex-col">
|
4
|
+ <div class="-my-2 overflow-x-auto">
|
5
|
+ <div class="py-2 align-middle inline-block min-w-full">
|
6
|
+ <div class="shadow overflow-hidden border-b border-slate-200 sm:rounded-lg">
|
7
|
+ <table class="min-w-full w-full divide-y divide-slate-200">
|
8
|
+ <thead class="bg-slate-50 dark:bg-stone-900">
|
9
|
+ <tr>
|
10
|
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-slate-500 dark:text-content-light uppercase tracking-wider">
|
11
|
+ Name
|
12
|
+ </th>
|
13
|
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-slate-500 dark:text-content-light uppercase tracking-wider">
|
14
|
+ Description
|
15
|
+ </th>
|
16
|
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-slate-500 dark:text-content-light uppercase tracking-wider">
|
17
|
+ Color
|
18
|
+ </th>
|
19
|
+ </tr>
|
20
|
+ </thead>
|
21
|
+ <tbody class="bg-white dark:bg-stone-800 divide-y divide-slate-200">
|
22
|
+ <%= for application_source <- @application_sources do %>
|
23
|
+ <tr class="cursor-pointer hover:bg-slate-50 hover:dark:bg-base-dark transition-all" phx-click="edit_application_source" phx-value-id={application_source.id} phx-target={@myself}>
|
24
|
+ <td class="flex px-6 py-4">
|
25
|
+ <div class="text-sm font-medium text-primary-dark dark:text-accent-dark truncate"><%= String.slice(application_source.name, 0..30) %></div>
|
26
|
+ </td>
|
27
|
+ <td>
|
28
|
+ <div class="text-sm font-medium text-primary-dark dark:text-accent-dark truncate">
|
29
|
+ <%= application_source.description %>
|
30
|
+ </div>
|
31
|
+ </td>
|
32
|
+ <td>
|
33
|
+ <div class="text-sm font-medium text-primary-dark dark:text-accent-dark truncate">
|
34
|
+ <span class="inline-flex items-center px-2.5 py-1 rounded-full uppercase text-xs border border-primary text-white font-bold" style={"background-color:#{application_source.color};"}>
|
35
|
+ <%= application_source.color %>
|
36
|
+ </span>
|
37
|
+ </div>
|
38
|
+ </td>
|
39
|
+ </tr>
|
40
|
+ <% end %>
|
41
|
+ </tbody>
|
42
|
+ </table>
|
43
|
+ </div>
|
44
|
+ </div>
|
45
|
+ </div>
|
46
|
+ </div>
|
47
|
+ </div>
|
48
|
+ </div>
|
|
\ No newline at end of file
|
changed
lib/kanta_web/live/translations/context_live/context_live.ex
|
@@ -1,18 +1,25 @@
|
1
1
|
defmodule KantaWeb.Translations.ContextLive do
|
2
2
|
use KantaWeb, :live_view
|
3
3
|
|
4
|
+ import Kanta.Utils.ParamParsers, only: [parse_id_filter: 1]
|
5
|
+
|
4
6
|
alias Kanta.Translations
|
5
7
|
alias Kanta.Translations.Context
|
6
8
|
|
7
9
|
def mount(%{"id" => id}, _session, socket) do
|
8
|
- context =
|
9
|
- case Translations.get_context(filter: [id: id]) do
|
10
|
- {:ok, %Context{} = context} -> context
|
11
|
- {:error, _, _reason} -> nil
|
10
|
+ socket =
|
11
|
+ case get_context(id) do
|
12
|
+ {:ok, %Context{} = context} -> assign(socket, :context, context)
|
13
|
+ {:error, _, _reason} -> redirect(socket, to: "/kanta/contexts")
|
12
14
|
end
|
13
15
|
|
14
|
- socket = socket |> assign(:context, context)
|
15
|
-
|
16
16
|
{:ok, socket}
|
17
17
|
end
|
18
|
+
|
19
|
+ defp get_context(id) do
|
20
|
+ case parse_id_filter(id) do
|
21
|
+ {:ok, id} -> Translations.get_context(filter: [id: id])
|
22
|
+ _ -> {:error, :id, :invalid}
|
23
|
+ end
|
24
|
+ end
|
18
25
|
end
|
changed
lib/kanta_web/live/translations/context_live/context_live.html.heex
|
@@ -1,11 +1,8 @@
|
1
1
|
<div>
|
2
2
|
<div>
|
3
3
|
<nav class="sm:hidden" aria-label="Back">
|
4
|
- <.link navigate={unverified_path(@socket, Kanta.Router, "/kanta/contexts")} class="flex items-center text-sm font-medium text-slate-500 hover:text-slate-700">
|
5
|
- <!-- Heroicon name: solid/chevron-left -->
|
6
|
- <svg class="flex-shrink-0 -ml-1 mr-1 h-5 w-5 text-slate-400" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
7
|
- <path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" />
|
8
|
- </svg>
|
4
|
+ <.link navigate={dashboard_path(@socket, "/contexts")} class="flex items-center text-sm font-medium text-slate-500 hover:text-slate-700">
|
5
|
+ <Icons.chevron_left class="flex-shrink-0 -ml-1 mr-1 h-5 w-5 text-slate-400" aria-hidden="true" />
|
9
6
|
Back
|
10
7
|
</.link>
|
11
8
|
</nav>
|
|
@@ -13,15 +10,12 @@
|
13
10
|
<ol class="flex items-center space-x-4">
|
14
11
|
<li>
|
15
12
|
<div>
|
16
|
- <.link navigate={unverified_path(@socket, Kanta.Router, "/kanta/contexts")} class="text-sm font-medium text-slate-500 hover:text-slate-700">Contexts</.link>
|
13
|
+ <.link navigate={dashboard_path(@socket, "/contexts")} class="text-sm font-medium text-slate-500 hover:text-slate-700">Contexts</.link>
|
17
14
|
</div>
|
18
15
|
</li>
|
19
16
|
<li>
|
20
17
|
<div class="flex items-center">
|
21
|
- <!-- Heroicon name: solid/chevron-right -->
|
22
|
- <svg class="flex-shrink-0 h-5 w-5 text-slate-400" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
23
|
- <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
24
|
- </svg>
|
18
|
+ <Icons.chevron_right class="flex-shrink-0 h-5 w-5 text-slate-400" aria-hidden="true" />
|
25
19
|
<.link navigate="#" aria-current="page" class="ml-4 text-sm font-medium text-slate-500 hover:text-slate-700"><%= @context.name %></.link>
|
26
20
|
</div>
|
27
21
|
</li>
|
|
@@ -35,4 +29,4 @@
|
35
29
|
</h2>
|
36
30
|
</div>
|
37
31
|
</div>
|
38
|
- </div>
|
|
\ No newline at end of file
|
32
|
+ </div>
|
changed
lib/kanta_web/live/translations/contexts_live/components/contexts_table/contexts_table.ex
|
@@ -10,14 +10,6 @@ defmodule KantaWeb.Translations.ContextsTable do
|
10
10
|
end
|
11
11
|
|
12
12
|
def handle_event("edit_context", %{"id" => id}, socket) do
|
13
|
- {:noreply,
|
14
|
- push_navigate(socket,
|
15
|
- to:
|
16
|
- unverified_path(
|
17
|
- socket,
|
18
|
- Kanta.Router,
|
19
|
- "/kanta/contexts/#{id}"
|
20
|
- )
|
21
|
- )}
|
13
|
+ {:noreply, push_navigate(socket, to: dashboard_path(socket, "/contexts/#{id}"))}
|
22
14
|
end
|
23
15
|
end
|
changed
lib/kanta_web/live/translations/contexts_live/contexts_live.ex
|
@@ -18,7 +18,7 @@ defmodule KantaWeb.Translations.ContextsLive do
|
18
18
|
end
|
19
19
|
|
20
20
|
def handle_event("navigate", %{"to" => to}, socket) do
|
21
|
- {:noreply, push_redirect(socket, to: "/kanta" <> to)}
|
21
|
+ {:noreply, push_redirect(socket, to: dashboard_path(socket) <> to)}
|
22
22
|
end
|
23
23
|
|
24
24
|
def handle_event("page_changed", %{"index" => page_number}, socket) do
|
changed
lib/kanta_web/live/translations/domain_live/domain_live.ex
|
@@ -1,18 +1,25 @@
|
1
1
|
defmodule KantaWeb.Translations.DomainLive do
|
2
2
|
use KantaWeb, :live_view
|
3
3
|
|
4
|
+ import Kanta.Utils.ParamParsers, only: [parse_id_filter: 1]
|
5
|
+
|
4
6
|
alias Kanta.Translations
|
5
7
|
alias Kanta.Translations.Domain
|
6
8
|
|
7
9
|
def mount(%{"id" => id}, _session, socket) do
|
8
|
- domain =
|
9
|
- case Translations.get_domain(filter: [id: id]) do
|
10
|
- {:ok, %Domain{} = domain} -> domain
|
11
|
- {:error, _, _reason} -> nil
|
10
|
+ socket =
|
11
|
+ case get_domain(id) do
|
12
|
+ {:ok, %Domain{} = domain} -> assign(socket, :domain, domain)
|
13
|
+ {:error, _, _reason} -> redirect(socket, to: "/kanta/domains")
|
12
14
|
end
|
13
15
|
|
14
|
- socket = socket |> assign(:domain, domain)
|
15
|
-
|
16
16
|
{:ok, socket}
|
17
17
|
end
|
18
|
+
|
19
|
+ defp get_domain(id) do
|
20
|
+ case parse_id_filter(id) do
|
21
|
+ {:ok, id} -> Translations.get_domain(filter: [id: id])
|
22
|
+ _ -> {:error, :id, :invalid}
|
23
|
+ end
|
24
|
+ end
|
18
25
|
end
|
changed
lib/kanta_web/live/translations/domain_live/domain_live.html.heex
|
@@ -1,11 +1,8 @@
|
1
1
|
<div>
|
2
2
|
<div>
|
3
3
|
<nav class="sm:hidden" aria-label="Back">
|
4
|
- <.link navigate={unverified_path(@socket, Kanta.Router, "/kanta/domains")} class="flex items-center text-sm font-medium text-slate-500 hover:text-slate-700">
|
5
|
- <!-- Heroicon name: solid/chevron-left -->
|
6
|
- <svg class="flex-shrink-0 -ml-1 mr-1 h-5 w-5 text-slate-400" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
7
|
- <path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" />
|
8
|
- </svg>
|
4
|
+ <.link navigate={dashboard_path(@socket, "/domains")} class="flex items-center text-sm font-medium text-slate-500 hover:text-slate-700">
|
5
|
+ <Icons.chevron_left class="flex-shrink-0 -ml-1 mr-1 h-5 w-5 text-slate-400" aria-hidden="true" />
|
9
6
|
Back
|
10
7
|
</.link>
|
11
8
|
</nav>
|
|
@@ -13,15 +10,12 @@
|
13
10
|
<ol class="flex items-center space-x-4">
|
14
11
|
<li>
|
15
12
|
<div>
|
16
|
- <.link navigate={unverified_path(@socket, Kanta.Router, "/kanta/domains")} class="text-sm font-medium text-slate-500 hover:text-slate-700">Domains</.link>
|
13
|
+ <.link navigate={dashboard_path(@socket, "/domains")} class="text-sm font-medium text-slate-500 hover:text-slate-700">Domains</.link>
|
17
14
|
</div>
|
18
15
|
</li>
|
19
16
|
<li>
|
20
17
|
<div class="flex items-center">
|
21
|
- <!-- Heroicon name: solid/chevron-right -->
|
22
|
- <svg class="flex-shrink-0 h-5 w-5 text-slate-400" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
23
|
- <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
24
|
- </svg>
|
18
|
+ <Icons.chevron_right class="flex-shrink-0 h-5 w-5 text-slate-400" aria-hidden="true" />
|
25
19
|
<.link navigate="#" aria-current="page" class="ml-4 text-sm font-medium text-slate-500 hover:text-slate-700"><%= @domain.name %></.link>
|
26
20
|
</div>
|
27
21
|
</li>
|
|
@@ -35,4 +29,4 @@
|
35
29
|
</h2>
|
36
30
|
</div>
|
37
31
|
</div>
|
38
|
- </div>
|
|
\ No newline at end of file
|
32
|
+ </div>
|
changed
lib/kanta_web/live/translations/domains_live/components/domains_table/domains_table.ex
|
@@ -10,14 +10,6 @@ defmodule KantaWeb.Translations.DomainsTable do
|
10
10
|
end
|
11
11
|
|
12
12
|
def handle_event("edit_domain", %{"id" => id}, socket) do
|
13
|
- {:noreply,
|
14
|
- push_navigate(socket,
|
15
|
- to:
|
16
|
- unverified_path(
|
17
|
- socket,
|
18
|
- Kanta.Router,
|
19
|
- "/kanta/domains/#{id}"
|
20
|
- )
|
21
|
- )}
|
13
|
+ {:noreply, push_navigate(socket, to: dashboard_path(socket, "/domains/#{id}"))}
|
22
14
|
end
|
23
15
|
end
|
changed
lib/kanta_web/live/translations/domains_live/domains_live.ex
|
@@ -18,7 +18,7 @@ defmodule KantaWeb.Translations.DomainsLive do
|
18
18
|
end
|
19
19
|
|
20
20
|
def handle_event("navigate", %{"to" => to}, socket) do
|
21
|
- {:noreply, push_redirect(socket, to: "/kanta" <> to)}
|
21
|
+ {:noreply, push_redirect(socket, to: dashboard_path(socket) <> to)}
|
22
22
|
end
|
23
23
|
|
24
24
|
def handle_event("page_changed", %{"index" => page_number}, socket) do
|
changed
lib/kanta_web/live/translations/locales_live/locales_live.html.heex
|
@@ -10,7 +10,7 @@
|
10
10
|
<div class="col-span-1 my-1 w-full">
|
11
11
|
<div>
|
12
12
|
<div class="w-full">
|
13
|
- <.link navigate={unverified_path(@socket, Kanta.Router, "/kanta/locales/#{locale.id}/translations")}>
|
13
|
+ <.link navigate={dashboard_path(@socket, "/locales/#{locale.id}/translations")}>
|
14
14
|
<div class="w-full bg-white dark:bg-stone-900 dark:border dark:border-accent-dark transition-all hover:bg-slate-100 overflow-hidden shadow rounded-lg items-center cursor-pointer">
|
15
15
|
<div class="flex flex-row justify-start px-4 py-5 sm:p-6 font-medium text-md text-slate-700">
|
16
16
|
<div class="relative w-12 h-12">
|
changed
lib/kanta_web/live/translations/translation_form_live/components/plural_translation_form/plural_translation_form.ex
|
@@ -65,11 +65,7 @@ defmodule KantaWeb.Translations.PluralTranslationForm do
|
65
65
|
{:noreply,
|
66
66
|
push_redirect(socket,
|
67
67
|
to:
|
68
|
- unverified_path(
|
69
|
- socket,
|
70
|
- Kanta.Router,
|
71
|
- "/kanta/locales/#{locale.id}/translations"
|
72
|
- )
|
68
|
+ dashboard_path(socket, "/locales/#{locale.id}/translations" <> get_query(socket.assigns))
|
73
69
|
)}
|
74
70
|
end
|
75
71
|
|
|
@@ -80,4 +76,11 @@ defmodule KantaWeb.Translations.PluralTranslationForm do
|
80
76
|
|> Map.fetch!(index)
|
81
77
|
|> Enum.join(", ")
|
82
78
|
end
|
79
|
+
|
80
|
+ defp get_query(%{filters: nil}), do: ""
|
81
|
+
|
82
|
+ defp get_query(%{filters: filters}) do
|
83
|
+ query = UriQuery.params(filters)
|
84
|
+ "?" <> URI.encode_query(query)
|
85
|
+ end
|
83
86
|
end
|
changed
lib/kanta_web/live/translations/translation_form_live/components/plural_translation_form/plural_translation_form.html.heex
|
@@ -3,10 +3,8 @@
|
3
3
|
<div>
|
4
4
|
<div>
|
5
5
|
<nav class="sm:hidden" aria-label="Back">
|
6
|
- <.link navigate={unverified_path(@socket, Kanta.Router, "/kanta/locales/#{@locale.id}/translations")} class="flex items-center text-sm font-medium text-slate-400 hover:text-slate-200">
|
7
|
- <svg class="flex-shrink-0 -ml-1 mr-1 h-5 w-5 text-slate-500" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
8
|
- <path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd"></path>
|
9
|
- </svg>
|
6
|
+ <.link navigate={dashboard_path(@socket, "/locales/#{@locale.id}/translations")} class="flex items-center text-sm font-medium text-slate-400 hover:text-slate-200">
|
7
|
+ <Icons.chevron_left class="flex-shrink-0 -ml-1 mr-1 h-5 w-5 text-slate-500" aria-hidden="true" />
|
10
8
|
Back
|
11
9
|
</.link>
|
12
10
|
</nav>
|
|
@@ -14,22 +12,18 @@
|
14
12
|
<ol class="flex items-center space-x-4">
|
15
13
|
<li>
|
16
14
|
<div>
|
17
|
- <.link patch={"/kanta/locales"} class="cursor-pointer text-sm font-medium text-slate-400 hover:text-slate-200">Locales</.link>
|
15
|
+ <.link patch={"#{dashboard_path(@socket)}/locales"} class="cursor-pointer text-sm font-medium text-slate-400 hover:text-slate-200">Locales</.link>
|
18
16
|
</div>
|
19
17
|
</li>
|
20
18
|
<li>
|
21
19
|
<div class="flex items-center">
|
22
|
- <svg class="flex-shrink-0 h-5 w-5 text-slate-500" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
23
|
- <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
|
24
|
- </svg>
|
25
|
- <.link patch={"/kanta/locales/#{@locale.id}/translations"} class="cursor-pointer ml-4 text-sm font-medium text-slate-400 hover:text-slate-200"><%= @locale.native_name %></.link>
|
20
|
+ <Icons.chevron_right class="flex-shrink-0 h-5 w-5 text-slate-500" aria-hidden="true" />
|
21
|
+ <.link patch={"#{dashboard_path(@socket)}/locales/#{@locale.id}/translations"} class="cursor-pointer ml-4 text-sm font-medium text-slate-400 hover:text-slate-200"><%= @locale.native_name %></.link>
|
26
22
|
</div>
|
27
23
|
</li>
|
28
24
|
<li>
|
29
25
|
<div class="flex items-center">
|
30
|
- <svg class="flex-shrink-0 h-5 w-5 text-slate-500" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
31
|
- <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
|
32
|
- </svg>
|
26
|
+ <Icons.chevron_right class="flex-shrink-0 h-5 w-5 text-slate-500" aria-hidden="true" />
|
33
27
|
<a href="#" aria-current="page" class="cursor-pointer ml-4 text-sm font-medium text-slate-400 hover:text-slate-200 truncate"><%= @message.msgid %></a>
|
34
28
|
</div>
|
35
29
|
</li>
|
|
@@ -45,7 +39,7 @@
|
45
39
|
</div>
|
46
40
|
</div>
|
47
41
|
</div>
|
48
|
- <.live_component module={Tabs} id="tabs" tabs={@tabs} current_url={"/kanta/locales/#{@locale.id}/translations/#{@message.id}"} current_tab={@current_tab} />
|
42
|
+ <.live_component module={Tabs} id="tabs" tabs={@tabs} current_url={"#{dashboard_path(@socket)}/locales/#{@locale.id}/translations/#{@message.id}"} current_tab={@current_tab} />
|
49
43
|
<.form for={@form} phx-change="validate" phx-submit="submit" phx-target={@myself} class="bg-white dark:bg-stone-900 shadow rounded-md mt-4 px-4 py-4 space-y-4">
|
50
44
|
<div class="pb-5 border-b border-slate-200 sm:flex sm:items-center sm:justify-between">
|
51
45
|
<h3 class="text-lg leading-6 font-medium text-primary-dark dark:text-accent-dark">
|
changed
lib/kanta_web/live/translations/translation_form_live/components/singular_translation_form/singular_translation_form.ex
|
@@ -31,11 +31,14 @@ defmodule KantaWeb.Translations.SingularTranslationForm do
|
31
31
|
{:noreply,
|
32
32
|
push_redirect(socket,
|
33
33
|
to:
|
34
|
- unverified_path(
|
35
|
- socket,
|
36
|
- Kanta.Router,
|
37
|
- "/kanta/locales/#{locale.id}/translations"
|
38
|
- )
|
34
|
+ dashboard_path(socket, "/locales/#{locale.id}/translations" <> get_query(socket.assigns))
|
39
35
|
)}
|
40
36
|
end
|
37
|
+
|
38
|
+ defp get_query(%{filters: nil}), do: ""
|
39
|
+
|
40
|
+ defp get_query(%{filters: filters}) do
|
41
|
+ query = UriQuery.params(filters)
|
42
|
+ "?" <> URI.encode_query(query)
|
43
|
+ end
|
41
44
|
end
|
changed
lib/kanta_web/live/translations/translation_form_live/components/singular_translation_form/singular_translation_form.html.heex
|
@@ -3,10 +3,8 @@
|
3
3
|
<div>
|
4
4
|
<div>
|
5
5
|
<nav class="sm:hidden" aria-label="Back">
|
6
|
- <.link navigate={unverified_path(@socket, Kanta.Router, "/kanta/locales/#{@locale.id}/translations")} class="flex items-center text-sm font-medium text-slate-400 hover:text-slate-200">
|
7
|
- <svg class="flex-shrink-0 -ml-1 mr-1 h-5 w-5 text-slate-500" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
8
|
- <path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd"></path>
|
9
|
- </svg>
|
6
|
+ <.link navigate={dashboard_path(@socket, "/locales/#{@locale.id}/translations")} class="flex items-center text-sm font-medium text-slate-400 hover:text-slate-200">
|
7
|
+ <Icons.chevron_left class="flex-shrink-0 -ml-1 mr-1 h-5 w-5 text-slate-500" aria-hidden="true" />
|
10
8
|
Back
|
11
9
|
</.link>
|
12
10
|
</nav>
|
|
@@ -14,22 +12,18 @@
|
14
12
|
<ol class="flex items-center space-x-4">
|
15
13
|
<li>
|
16
14
|
<div>
|
17
|
- <.link patch={"/kanta/locales"} class="cursor-pointer text-sm font-medium text-slate-400 hover:text-slate-200">Locales</.link>
|
15
|
+ <.link patch={"#{dashboard_path(@socket)}/locales"} class="cursor-pointer text-sm font-medium text-slate-400 hover:text-slate-200">Locales</.link>
|
18
16
|
</div>
|
19
17
|
</li>
|
20
18
|
<li>
|
21
19
|
<div class="flex items-center">
|
22
|
- <svg class="flex-shrink-0 h-5 w-5 text-slate-500" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
23
|
- <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
|
24
|
- </svg>
|
25
|
- <.link patch={"/kanta/locales/#{@locale.id}/translations"} class="cursor-pointer ml-4 text-sm font-medium text-slate-400 hover:text-slate-200"><%= @locale.native_name %></.link>
|
20
|
+ <Icons.chevron_right class="flex-shrink-0 h-5 w-5 text-slate-500" aria-hidden="true" />
|
21
|
+ <.link patch={"#{dashboard_path(@socket)}/locales/#{@locale.id}/translations"} class="cursor-pointer ml-4 text-sm font-medium text-slate-400 hover:text-slate-200"><%= @locale.native_name %></.link>
|
26
22
|
</div>
|
27
23
|
</li>
|
28
24
|
<li>
|
29
25
|
<div class="flex items-center">
|
30
|
- <svg class="flex-shrink-0 h-5 w-5 text-slate-500" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
31
|
- <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
|
32
|
- </svg>
|
26
|
+ <Icons.chevron_right class="flex-shrink-0 h-5 w-5 text-slate-500" aria-hidden="true" />
|
33
27
|
<a href="#" aria-current="page" class="cursor-pointer ml-4 text-sm font-medium text-slate-400 hover:text-slate-200"><%= String.slice(@message.msgid, 0..30) %></a>
|
34
28
|
</div>
|
35
29
|
</li>
|
changed
lib/kanta_web/live/translations/translation_form_live/translation_form_live.ex
|
@@ -1,6 +1,8 @@
|
1
1
|
defmodule KantaWeb.Translations.TranslationFormLive do
|
2
2
|
use KantaWeb, :live_view
|
3
3
|
|
4
|
+ import Kanta.Utils.ParamParsers, only: [parse_id_filter: 1]
|
5
|
+
|
4
6
|
alias Kanta.Translations
|
5
7
|
alias Kanta.Translations.Message
|
6
8
|
|
|
@@ -14,6 +16,7 @@ defmodule KantaWeb.Translations.TranslationFormLive do
|
14
16
|
translation={@translations}
|
15
17
|
message={@message}
|
16
18
|
locale={@locale}
|
19
|
+ filters={@filters}
|
17
20
|
/>
|
18
21
|
"""
|
19
22
|
end
|
|
@@ -35,22 +38,25 @@ defmodule KantaWeb.Translations.TranslationFormLive do
|
35
38
|
locale={@locale}
|
36
39
|
current_tab={@tab}
|
37
40
|
current_tab_index={String.to_integer(@tab) - 1}
|
41
|
+ filters={@filters}
|
38
42
|
/>
|
39
43
|
"""
|
40
44
|
end
|
41
45
|
|
42
|
- def mount(%{"message_id" => message_id, "locale_id" => locale_id}, _session, socket) do
|
46
|
+ def mount(%{"message_id" => message_id, "locale_id" => locale_id} = params, _session, socket) do
|
43
47
|
socket =
|
44
|
- with {:ok, locale} <- Translations.get_locale(filter: [id: locale_id]),
|
45
|
- {:ok, message} <- Translations.get_message(filter: [id: message_id]),
|
48
|
+ with {:ok, locale} <- get_locale(locale_id),
|
49
|
+ {:ok, message} <- get_message(message_id),
|
46
50
|
{:ok, translations} <- get_translations(message, locale) do
|
47
51
|
socket
|
48
52
|
|> assign(:locale, locale)
|
49
53
|
|> assign(:message, message)
|
50
54
|
|> assign(:translations, translations)
|
55
|
+ else
|
56
|
+ _ -> redirect(socket, to: "/kanta/locales/#{locale_id}/translations")
|
51
57
|
end
|
52
58
|
|
53
|
- {:ok, socket}
|
59
|
+ {:ok, assign(socket, :filters, Map.get(params, "filters"))}
|
54
60
|
end
|
55
61
|
|
56
62
|
def handle_params(%{"tab" => tab}, _uri, socket) do
|
|
@@ -116,4 +122,18 @@ defmodule KantaWeb.Translations.TranslationFormLive do
|
116
122
|
end
|
117
123
|
end
|
118
124
|
end
|
125
|
+
|
126
|
+ defp get_locale(locale_id) do
|
127
|
+ case parse_id_filter(locale_id) do
|
128
|
+ {:ok, id} -> Translations.get_locale(filter: [id: id])
|
129
|
+ _ -> {:error, :id, :invalid}
|
130
|
+ end
|
131
|
+ end
|
132
|
+
|
133
|
+ defp get_message(message_id) do
|
134
|
+ case parse_id_filter(message_id) do
|
135
|
+ {:ok, id} -> Translations.get_message(filter: [id: id])
|
136
|
+ _ -> {:error, :id, :invalid}
|
137
|
+ end
|
138
|
+ end
|
119
139
|
end
|
changed
lib/kanta_web/live/translations/translations_live/components/filters_bar/filters_bar.ex
|
@@ -13,13 +13,25 @@ defmodule KantaWeb.Translations.Components.FiltersBar do
|
13
13
|
%{entries: contexts, metadata: _contexts_metadata} = Translations.list_contexts()
|
14
14
|
%{entries: domains, metadata: _domains_metadata} = Translations.list_domains()
|
15
15
|
|
16
|
+ %{entries: application_sources, metadata: _application_sources_metadata} =
|
17
|
+ Translations.list_application_sources()
|
18
|
+
|
19
|
+ {col_span, grid_cols} =
|
20
|
+ if assigns.application_sources_empty?,
|
21
|
+ do: {"col-span-5", "grid-cols-5"},
|
22
|
+ else: {"col-span-6", "grid-cols-6"}
|
23
|
+
|
16
24
|
socket =
|
17
25
|
socket
|
18
26
|
|> assign(:contexts, contexts)
|
19
27
|
|> assign(:domains, domains)
|
28
|
+ |> assign(:application_sources, application_sources)
|
29
|
+ |> assign(:col_span, col_span)
|
30
|
+ |> assign(:grid_cols, grid_cols)
|
20
31
|
|> assign(:filters, %{
|
21
32
|
domain: nil,
|
22
|
- context: nil
|
33
|
+ context: nil,
|
34
|
+ application_source: nil
|
23
35
|
})
|
24
36
|
|
25
37
|
{:ok, assign(socket, assigns)}
|
changed
lib/kanta_web/live/translations/translations_live/components/filters_bar/filters_bar.html.heex
|
@@ -1,6 +1,7 @@
|
1
|
- <div >
|
2
|
- <.form :let={form} for={@filters} phx-change="change" class="grid grid-cols-5 gap-2">
|
3
|
- <div class="col-span-5 sm:col-span-5 xl:col-span-2">
|
1
|
+ <div>
|
2
|
+ <.form :let={form} for={@filters} phx-change="change" class={"grid #{@grid_cols} gap-2"}>
|
3
|
+ <.link navigate={dashboard_path(@socket, "/locales/#{@locale_id}/translations")} class="absolute -top-1 right-0 font-semibold text-primary-dark dark:text-accent-dark">Clear filters</.link>
|
4
|
+ <div class={"#{@col_span} sm:#{@col_span} xl:col-span-2"}>
|
4
5
|
<SearchInput.render
|
5
6
|
type="text"
|
6
7
|
id={form["search"].id}
|
|
@@ -10,7 +11,7 @@
|
10
11
|
label="Search"
|
11
12
|
/>
|
12
13
|
</div>
|
13
|
- <div class="col-span-5 sm:col-span-2 xl:col-span-1">
|
14
|
+ <div class={"#{@col_span} sm:col-span-2 xl:col-span-1"}>
|
14
15
|
<.live_component
|
15
16
|
form={form}
|
16
17
|
module={Select}
|
|
@@ -20,7 +21,7 @@
|
20
21
|
options={[%{color: "#c3c3c3", label: "All", value: nil}] ++ Enum.map(@domains, & %{color: &1.color, label: &1.name, value: &1.id})}
|
21
22
|
/>
|
22
23
|
</div>
|
23
|
- <div class="col-span-5 sm:col-span-2 xl:col-span-1">
|
24
|
+ <div class={"#{@col_span} sm:col-span-2 xl:col-span-1"}>
|
24
25
|
<.live_component
|
25
26
|
form={form}
|
26
27
|
module={Select}
|
|
@@ -30,14 +31,27 @@
|
30
31
|
options={[%{color: "#c3c3c3", label: "All", value: nil}] ++ Enum.map(@contexts, & %{color: &1.color, label: &1.name, value: &1.id})}
|
31
32
|
/>
|
32
33
|
</div>
|
33
|
- <div class="col-span-5 sm:col-span-1 xl:col-span-1 mt-4 xl:mt-7">
|
34
|
+ <%= if not Enum.empty?(@application_sources) do %>
|
35
|
+ <div class={"#{@col_span} sm:col-span-2 xl:col-span-1"}>
|
36
|
+ <.live_component
|
37
|
+ form={form}
|
38
|
+ module={Select}
|
39
|
+ id={"application_source_id"}
|
40
|
+ label="Application"
|
41
|
+ field={form["application_source_id"]}
|
42
|
+ options={[%{color: "#c3c3c3", label: "All", value: nil}] ++ Enum.map(@application_sources, & %{color: &1.color, label: &1.name, value: &1.id})}
|
43
|
+ />
|
44
|
+ </div>
|
45
|
+ <% end %>
|
46
|
+ <div class={"#{@col_span} sm:col-span-1 xl:col-span-1 mt-4 xl:mt-7"}>
|
34
47
|
<.live_component
|
35
48
|
form={form}
|
36
49
|
module={Toggle}
|
37
50
|
id={"not_translated"}
|
51
|
+ default_value={@not_translated_default}
|
38
52
|
label="Not translated"
|
39
53
|
field={form["not_translated"]}
|
40
54
|
/>
|
41
55
|
</div>
|
42
56
|
</.form>
|
43
|
- </div>
|
|
\ No newline at end of file
|
57
|
+ </div>
|
changed
lib/kanta_web/live/translations/translations_live/components/messages_table/messages_table.ex
|
@@ -7,23 +7,29 @@ defmodule KantaWeb.Translations.Components.MessagesTable do
|
7
7
|
|
8
8
|
alias Kanta.Translations.{Message, SingularTranslation}
|
9
9
|
|
10
|
+ @available_params ~w(page search filter)
|
11
|
+ @params_in_filter ~w(domain_id context_id not_translated)
|
12
|
+
|
10
13
|
def update(socket, assigns) do
|
11
14
|
{:ok, assign(assigns, socket)}
|
12
15
|
end
|
13
16
|
|
14
17
|
def handle_event("edit_message", %{"id" => id}, socket) do
|
18
|
+ params = get_filter_params_from_assigns(socket.assigns)
|
19
|
+ query = UriQuery.params(params)
|
20
|
+
|
15
21
|
{:noreply,
|
16
22
|
push_navigate(socket,
|
17
23
|
to:
|
18
|
- unverified_path(
|
24
|
+ dashboard_path(
|
19
25
|
socket,
|
20
|
- Kanta.Router,
|
21
|
- "/kanta/locales/#{socket.assigns.locale.id}/translations/#{id}"
|
26
|
+ "/locales/#{socket.assigns.locale.id}/translations/#{id}?" <>
|
27
|
+ URI.encode_query(query)
|
22
28
|
)
|
23
29
|
)}
|
24
30
|
end
|
25
31
|
|
26
|
- def is_translated(%Message{message_type: :singular} = message, locale, source) do
|
32
|
+ def translated?(%Message{message_type: :singular} = message, locale, source) do
|
27
33
|
case Enum.find(message.singular_translations, &(&1.locale_id == locale.id)) do
|
28
34
|
nil ->
|
29
35
|
false
|
|
@@ -37,18 +43,18 @@ defmodule KantaWeb.Translations.Components.MessagesTable do
|
37
43
|
end
|
38
44
|
end
|
39
45
|
|
40
|
- def is_translated(%Message{message_type: :plural} = message, locale, source) do
|
46
|
+ def translated?(%Message{message_type: :plural} = message, locale, source) do
|
41
47
|
case Enum.filter(message.plural_translations, &(&1.locale_id == locale.id)) do
|
42
48
|
[] ->
|
43
49
|
false
|
44
50
|
|
45
51
|
translations ->
|
46
52
|
translations
|
47
|
- |> Enum.map(&is_plural_form_translated(&1, source))
|
53
|
+ |> Enum.map(&plural_form_translated?(&1, source))
|
48
54
|
end
|
49
55
|
end
|
50
56
|
|
51
|
- defp is_plural_form_translated(translation, source) do
|
57
|
+ defp plural_form_translated?(translation, source) do
|
52
58
|
case get_in(translation, [Access.key!(source)]) do
|
53
59
|
nil ->
|
54
60
|
false
|
|
@@ -135,4 +141,19 @@ defmodule KantaWeb.Translations.Components.MessagesTable do
|
135
141
|
defp truncate_translation(text) do
|
136
142
|
if String.length(text) > 45, do: String.slice(text, 0..45) <> "... ", else: text
|
137
143
|
end
|
144
|
+
|
145
|
+ defp get_filter_params_from_assigns(%{filters: filters}) do
|
146
|
+ filter =
|
147
|
+ filters
|
148
|
+ |> Map.take(@params_in_filter)
|
149
|
+ |> Map.reject(fn {_, value} -> is_nil(value) or value == "" end)
|
150
|
+
|
151
|
+ params =
|
152
|
+ filters
|
153
|
+ |> Map.take(@available_params)
|
154
|
+ |> Map.put("filter", filter)
|
155
|
+ |> Map.reject(fn {_, value} -> is_nil(value) or value == "" end)
|
156
|
+
|
157
|
+ %{"filters" => params}
|
158
|
+ end
|
138
159
|
end
|
changed
lib/kanta_web/live/translations/translations_live/components/messages_table/messages_table.html.heex
|
@@ -22,6 +22,11 @@
|
22
22
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-slate-500 dark:text-content-light uppercase tracking-wider">
|
23
23
|
Context
|
24
24
|
</th>
|
25
|
+ <%= if not @application_sources_empty? do %>
|
26
|
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-slate-500 dark:text-content-light uppercase tracking-wider">
|
27
|
+ Application
|
28
|
+ </th>
|
29
|
+ <% end %>
|
25
30
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-slate-500 dark:text-content-light uppercase tracking-wider">
|
26
31
|
Type
|
27
32
|
</th>
|
|
@@ -36,7 +41,7 @@
|
36
41
|
<td class="px-6 py-4">
|
37
42
|
<div class={[
|
38
43
|
"text-md font-medium tracking-wide",
|
39
|
- "#{if is_translated(message, @locale, :original_text), do: "text-green-700 dark:text-green-500", else: "text-red-700 dark:text-red-500"}"
|
44
|
+ "#{if translated?(message, @locale, :original_text), do: "text-green-700 dark:text-green-500", else: "text-red-700 dark:text-red-500"}"
|
40
45
|
]}>
|
41
46
|
<%= original_text(assigns, message) %>
|
42
47
|
</div>
|
|
@@ -44,14 +49,14 @@
|
44
49
|
<td class="px-6 py-4">
|
45
50
|
<div class={[
|
46
51
|
"text-md font-medium tracking-wide",
|
47
|
- "#{if is_translated(message, @locale, :translated_text), do: "text-green-700 dark:text-green-500", else: "text-red-700 dark:text-red-500"}"
|
52
|
+ "#{if translated?(message, @locale, :translated_text), do: "text-green-700 dark:text-green-500", else: "text-red-700 dark:text-red-500"}"
|
48
53
|
]}>
|
49
54
|
<%= translated_text(assigns, message) %>
|
50
55
|
</div>
|
51
56
|
</td>
|
52
57
|
<td class="px-6 py-4">
|
53
58
|
<div class="text-sm font-medium text-primary-dark truncate">
|
54
|
- <span class={"inline-flex items-center px-2.5 py-1 rounded-full uppercase text-xs border border-primary dark:border-accent-dark #{if is_nil(message.domain), do: "text-primary-dark dark:text-accent-dark", else: "text-white"} font-bold"} style={"background-color:#{message.domain.color};"}>
|
59
|
+ <span class={"inline-flex items-center px-2.5 py-1 rounded-full uppercase text-xs border border-primary dark:border-accent-dark #{if is_nil(message.domain), do: "text-primary-dark dark:text-accent-dark", else: "text-white"} font-bold"} style={"background-color:#{unless is_nil(message.domain), do: message.domain.color, else: "transparent"};"}>
|
55
60
|
<%= if is_nil(message.domain), do: "None", else: message.domain.name %>
|
56
61
|
</span>
|
57
62
|
</div>
|
|
@@ -63,6 +68,15 @@
|
63
68
|
</span>
|
64
69
|
</div>
|
65
70
|
</td>
|
71
|
+ <%= if not @application_sources_empty? do %>
|
72
|
+ <td class="px-6 py-4">
|
73
|
+ <div class="text-sm font-medium text-primary-dark truncate">
|
74
|
+ <span class={"inline-flex items-center px-2.5 py-1 rounded-full uppercase text-xs border border-primary dark:border-accent-dark #{if is_nil(message.application_source), do: "text-primary-dark dark:text-accent-dark", else: "text-white"} font-bold"} style={"background-color:#{unless is_nil(message.application_source), do: message.application_source.color, else: "transparent"};"}>
|
75
|
+ <%= if is_nil(message.application_source), do: "None", else: message.application_source.name %>
|
76
|
+ </span>
|
77
|
+ </div>
|
78
|
+ </td>
|
79
|
+ <% end %>
|
66
80
|
<td class="px-6 py-4">
|
67
81
|
<span class="inline-flex items-center px-2.5 py-1 rounded-full uppercase text-xs border border-primary dark:border-accent-dark text-primary-dark dark:text-accent-dark font-bold">
|
68
82
|
<%= message.message_type %>
|
|
@@ -77,4 +91,4 @@
|
77
91
|
</div>
|
78
92
|
</div>
|
79
93
|
</div>
|
80
|
- </div>
|
|
\ No newline at end of file
|
94
|
+ </div>
|
changed
lib/kanta_web/live/translations/translations_live/translations_live.ex
|
@@ -1,41 +1,57 @@
|
1
1
|
defmodule KantaWeb.Translations.TranslationsLive do
|
2
2
|
use KantaWeb, :live_view
|
3
3
|
|
4
|
+ import Kanta.Utils.ParamParsers
|
5
|
+
|
4
6
|
alias Kanta.Translations
|
7
|
+ alias Kanta.Translations.SingularTranslations.Finders.ListSingularTranslations
|
8
|
+ alias Kanta.Translations.PluralTranslations.Finders.ListPluralTranslations
|
5
9
|
alias KantaWeb.Translations.Components.{FiltersBar, MessagesTable}
|
6
10
|
|
7
11
|
alias KantaWeb.Components.Shared.Pagination
|
8
12
|
|
9
|
- @available_filters ~w(domain_id context_id search not_translated page)
|
13
|
+ @available_filters ~w(application_source_id domain_id context_id search not_translated page)
|
14
|
+ @available_params ~w(page search filter)
|
15
|
+ @params_in_filter ~w(application_source_id domain_id context_id not_translated)
|
16
|
+ @ids_to_parse ~w(application_source_id domain_id context_id locale_id)
|
10
17
|
|
11
|
- def mount(%{"locale_id" => locale_id}, _session, socket) do
|
18
|
+ def mount(%{"locale_id" => locale_id} = params, _session, socket) do
|
12
19
|
socket =
|
13
|
- case Translations.get_locale(filter: [id: locale_id]) do
|
20
|
+ case get_locale(locale_id) do
|
14
21
|
{:ok, locale} ->
|
15
22
|
socket
|
16
23
|
|> assign(:locale, locale)
|
17
|
- |> assign(:filters, %{})
|
24
|
+ |> assign(:application_sources_empty?, Translations.application_sources_empty?())
|
25
|
+ |> assign(get_assigns_from_params(params))
|
18
26
|
|
19
27
|
_ ->
|
20
28
|
socket
|
29
|
+ |> redirect(to: "/kanta/locales")
|
21
30
|
end
|
22
31
|
|
23
32
|
{:ok, socket}
|
24
33
|
end
|
25
34
|
|
26
35
|
def handle_params(%{"locale_id" => locale_id} = params, _location, socket) do
|
36
|
+ preload_filters = %{"locale_id" => locale_id}
|
37
|
+ singular_translation_query = ListSingularTranslations.filter_query(preload_filters)
|
38
|
+ plural_translation_query = ListPluralTranslations.filter_query(preload_filters)
|
39
|
+
|
27
40
|
%{entries: messages, metadata: messages_metadata} =
|
28
41
|
Translations.list_messages(
|
29
42
|
[]
|
30
|
- |> Keyword.merge(filter: Map.put(params["filter"] || %{}, "locale_id", locale_id))
|
31
43
|
|> Keyword.merge(search: params["search"] || "")
|
32
|
- |> Keyword.merge(page: params["page"] || "1")
|
44
|
+ |> Keyword.merge(page: parse_page(params["page"] || "1"))
|
45
|
+ |> Keyword.merge(
|
46
|
+ filter: parse_filters(Map.put(params["filter"] || %{}, "locale_id", locale_id))
|
47
|
+ )
|
33
48
|
|> Keyword.merge(
|
34
49
|
preloads: [
|
50
|
+ :application_source,
|
35
51
|
:context,
|
36
52
|
:domain,
|
37
|
- :singular_translations,
|
38
|
- :plural_translations
|
53
|
+ singular_translations: singular_translation_query,
|
54
|
+ plural_translations: plural_translation_query
|
39
55
|
]
|
40
56
|
)
|
41
57
|
)
|
|
@@ -49,6 +65,7 @@ defmodule KantaWeb.Translations.TranslationsLive do
|
49
65
|
end
|
50
66
|
|
51
67
|
def handle_event("change", filters, socket) do
|
68
|
+ filters = Map.put(filters, "page", "1")
|
52
69
|
query = UriQuery.params(format_filters(Map.merge(socket.assigns.filters, filters)))
|
53
70
|
|
54
71
|
socket = socket |> assign(:filters, Map.merge(socket.assigns.filters, filters))
|
|
@@ -56,13 +73,13 @@ defmodule KantaWeb.Translations.TranslationsLive do
|
56
73
|
{:noreply,
|
57
74
|
push_patch(socket,
|
58
75
|
to:
|
59
|
- "/kanta/locales/#{socket.assigns.locale.id}/translations?" <>
|
76
|
+ "#{dashboard_path(socket)}/locales/#{socket.assigns.locale.id}/translations?" <>
|
60
77
|
URI.encode_query(query)
|
61
78
|
)}
|
62
79
|
end
|
63
80
|
|
64
81
|
def handle_event("navigate", %{"to" => to}, socket) do
|
65
|
- {:noreply, push_redirect(socket, to: "/kanta" <> to)}
|
82
|
+ {:noreply, push_redirect(socket, to: dashboard_path(socket) <> to)}
|
66
83
|
end
|
67
84
|
|
68
85
|
def handle_event("page_changed", %{"index" => page_number}, socket) do
|
|
@@ -70,50 +87,92 @@ defmodule KantaWeb.Translations.TranslationsLive do
|
70
87
|
socket
|
71
88
|
|> assign(
|
72
89
|
:filters,
|
73
|
- Map.merge(socket.assigns.filters, %{"page" => String.to_integer(page_number)})
|
90
|
+ Map.merge(socket.assigns.filters, %{"page" => parse_page(page_number)})
|
74
91
|
)
|
75
92
|
|
76
93
|
query =
|
77
94
|
UriQuery.params(
|
78
|
- format_filters(
|
79
|
- Map.merge(socket.assigns.filters, %{"page" => String.to_integer(page_number)})
|
80
|
- )
|
95
|
+ format_filters(Map.merge(socket.assigns.filters, %{"page" => parse_page(page_number)}))
|
81
96
|
)
|
82
97
|
|
83
98
|
{:noreply,
|
84
99
|
push_patch(socket,
|
85
100
|
to:
|
86
|
- "/kanta/locales/#{socket.assigns.locale.id}/translations?" <>
|
101
|
+ "#{dashboard_path(socket)}/locales/#{socket.assigns.locale.id}/translations?" <>
|
87
102
|
URI.encode_query(query)
|
88
103
|
)}
|
89
104
|
end
|
90
105
|
|
106
|
+ defp get_locale(id) do
|
107
|
+ case parse_id_filter(id) do
|
108
|
+ {:ok, id} -> Translations.get_locale(filter: [id: id])
|
109
|
+ _ -> {:error, :id, :invalid}
|
110
|
+ end
|
111
|
+ end
|
112
|
+
|
91
113
|
defp format_filters(filters) do
|
92
114
|
filters
|
93
115
|
|> Map.take(@available_filters)
|
94
116
|
|> Enum.reject(fn {_, value} -> is_nil(value) or value == "" end)
|
95
|
- |> Enum.reduce([filter: %{}, search: "", page: "1"], fn {key, value}, acc ->
|
117
|
+ |> Enum.reduce([filter: %{}, search: "", page: "1"], &update_filters_acc/2)
|
118
|
+ end
|
119
|
+
|
120
|
+ defp update_filters_acc({"search", value}, acc), do: Keyword.put(acc, :search, value)
|
121
|
+ defp update_filters_acc({"page", value}, acc), do: Keyword.put(acc, :page, value)
|
122
|
+
|
123
|
+ defp update_filters_acc({"not_translated", value}, acc) do
|
124
|
+ Keyword.put(acc, :filter, Map.put(acc[:filter] || %{}, "not_translated", value))
|
125
|
+ end
|
126
|
+
|
127
|
+ defp update_filters_acc({key, value}, acc) do
|
128
|
+ case parse_id_filter(value) do
|
129
|
+ {:ok, id} -> Keyword.put(acc, :filter, Map.put(acc[:filter] || %{}, key, id))
|
130
|
+ _ -> acc
|
131
|
+ end
|
132
|
+ end
|
133
|
+
|
134
|
+ defp get_assigns_from_params(params) do
|
135
|
+ params
|
136
|
+ |> Map.take(@available_params)
|
137
|
+ |> Enum.reduce(%{}, fn {key, value}, acc ->
|
96
138
|
case key do
|
97
|
- "search" ->
|
98
|
- Keyword.put(acc, :search, value)
|
99
|
-
|
100
|
- "page" ->
|
101
|
- Keyword.put(acc, :page, value)
|
102
|
-
|
103
|
- "not_translated" ->
|
104
|
- Keyword.put(
|
105
|
- acc,
|
106
|
- :filter,
|
107
|
- Map.put(acc[:filter] || %{}, "not_translated", value)
|
108
|
- )
|
139
|
+ "filter" ->
|
140
|
+ values = Map.take(value, @params_in_filter)
|
141
|
+ Map.merge(acc, values)
|
109
142
|
|
110
143
|
filter_key ->
|
111
|
- Keyword.put(
|
112
|
- acc,
|
113
|
- :filter,
|
114
|
- Map.put(acc[:filter] || %{}, filter_key, String.to_integer(value))
|
115
|
- )
|
144
|
+ Map.put(acc, filter_key, value)
|
116
145
|
end
|
117
146
|
end)
|
147
|
+ |> then(fn filters ->
|
148
|
+ %{
|
149
|
+ not_translated_default: get_not_translated_default_value(params),
|
150
|
+ filters: filters
|
151
|
+ }
|
152
|
+ end)
|
153
|
+ end
|
154
|
+
|
155
|
+ defp get_not_translated_default_value(%{"filter" => filter}) do
|
156
|
+ case filter["not_translated"] do
|
157
|
+ "true" -> true
|
158
|
+ _ -> false
|
159
|
+ end
|
160
|
+ end
|
161
|
+
|
162
|
+ defp get_not_translated_default_value(_), do: false
|
163
|
+
|
164
|
+ defp parse_filters(filters) do
|
165
|
+ Enum.reduce(filters, %{}, &parse_filter/2)
|
166
|
+ end
|
167
|
+
|
168
|
+ defp parse_filter({key, value}, acc) when key in @ids_to_parse do
|
169
|
+ case parse_id_filter(value) do
|
170
|
+ {:ok, id} -> Map.put(acc, key, id)
|
171
|
+ _ -> acc
|
172
|
+ end
|
173
|
+ end
|
174
|
+
|
175
|
+ defp parse_filter({key, value}, acc) do
|
176
|
+ Map.put(acc, key, value)
|
118
177
|
end
|
119
178
|
end
|
changed
lib/kanta_web/live/translations/translations_live/translations_live.html.heex
|
@@ -2,11 +2,8 @@
|
2
2
|
<div>
|
3
3
|
<div>
|
4
4
|
<nav class="sm:hidden" aria-label="Back">
|
5
|
- <.link navigate={unverified_path(@socket, Kanta.Router, "/kanta/locales")} class="flex items-center text-sm font-medium text-slate-500 hover:text-slate-700">
|
6
|
- <!-- Heroicon name: solid/chevron-left -->
|
7
|
- <svg class="flex-shrink-0 -ml-1 mr-1 h-5 w-5 text-slate-400 dark:text-content-light" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
8
|
- <path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" />
|
9
|
- </svg>
|
5
|
+ <.link navigate={dashboard_path(@socket, "/locales")} class="flex items-center text-sm font-medium text-slate-500 hover:text-slate-700">
|
6
|
+ <Icons.chevron_left class="flex-shrink-0 -ml-1 mr-1 h-5 w-5 text-slate-400 dark:text-content-light" aria-hidden="true" />
|
10
7
|
Back
|
11
8
|
</.link>
|
12
9
|
</nav>
|
|
@@ -14,15 +11,12 @@
|
14
11
|
<ol class="flex items-center space-x-4">
|
15
12
|
<li>
|
16
13
|
<div>
|
17
|
- <.link navigate={unverified_path(@socket, Kanta.Router, "/kanta/locales")} class="text-sm font-medium text-slate-500 dark:text-content-light hover:text-slate-700 dark:hover:text-white">Locales</.link>
|
14
|
+ <.link navigate={dashboard_path(@socket, "/locales")} class="text-sm font-medium text-slate-500 dark:text-content-light hover:text-slate-700 dark:hover:text-white">Locales</.link>
|
18
15
|
</div>
|
19
16
|
</li>
|
20
17
|
<li>
|
21
18
|
<div class="flex items-center">
|
22
|
- <!-- Heroicon name: solid/chevron-right -->
|
23
|
- <svg class="flex-shrink-0 h-5 w-5 text-slate-400" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
24
|
- <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
25
|
- </svg>
|
19
|
+ <Icons.chevron_right class="flex-shrink-0 h-5 w-5 text-slate-400" aria-hidden="true" />
|
26
20
|
<.link navigate="#" aria-current="page" class="ml-4 text-sm font-medium text-slate-500 dark:text-content-light hover:text-slate-700 dark:hover:text-white"><%= @locale.native_name %></.link>
|
27
21
|
</div>
|
28
22
|
</li>
|
|
@@ -38,7 +32,7 @@
|
38
32
|
</div>
|
39
33
|
</div>
|
40
34
|
<div class="mt-4">
|
41
|
- <.live_component module={FiltersBar} filters={@filters} id="filters-bar" />
|
42
|
- <.live_component module={MessagesTable} id="messages-table" messages={@messages} locale={@locale} />
|
35
|
+ <.live_component module={FiltersBar} filters={@filters} not_translated_default={@not_translated_default} application_sources_empty?={@application_sources_empty?} locale_id={@locale.id} id="filters-bar" />
|
36
|
+ <.live_component module={MessagesTable} messages={@messages} filters={@filters} locale={@locale} application_sources_empty?={@application_sources_empty?} id="messages-table" />
|
43
37
|
<Pagination.render metadata={@messages_metadata} on_page_change="page_changed" />
|
44
|
- </div>
|
|
\ No newline at end of file
|
38
|
+ </div>
|
changed
lib/kanta_web/plugs/api_auth_plug.ex
|
@@ -8,7 +8,7 @@ defmodule KantaWeb.APIAuthPlug do
|
8
8
|
def init(_opts), do: %{}
|
9
9
|
|
10
10
|
def call(conn, _opts) do
|
11
|
- if api_authorization_disabled?() or is_bearer_token_valid?(conn) do
|
11
|
+ if api_authorization_disabled?() or bearer_token_valid?(conn) do
|
12
12
|
conn
|
13
13
|
else
|
14
14
|
conn
|
|
@@ -20,16 +20,16 @@ defmodule KantaWeb.APIAuthPlug do
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
- defp is_bearer_token_valid?(conn) do
|
23
|
+ defp bearer_token_valid?(conn) do
|
24
24
|
with {:ok, token} <- extract_bearer_token(conn),
|
25
|
- true <- is_secret_token_matching?(token) do
|
25
|
+ true <- secret_token_matching?(token) do
|
26
26
|
true
|
27
27
|
else
|
28
28
|
_ -> false
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
- defp is_secret_token_matching?(token) do
|
32
|
+ defp secret_token_matching?(token) do
|
33
33
|
secret_token_env =
|
34
34
|
@kanta_secret_token
|
35
35
|
|> System.get_env()
|
changed
lib/kanta_web/router.ex
|
@@ -25,13 +25,23 @@ defmodule KantaWeb.Router do
|
25
25
|
get "/css-:md5", KantaWeb.Assets, :css, as: :kanta_dashboard_asset
|
26
26
|
get "/js-:md5", KantaWeb.Assets, :js, as: :kanta_dashboard_asset
|
27
27
|
|
28
|
- redirect("/", "/kanta/dashboard", :permanent)
|
28
|
+ redirect(
|
29
|
+ "/",
|
30
|
+ "#{KantaWeb.Router.internal_dashboard_scoped_path(path)}/dashboard",
|
31
|
+ :permanent
|
32
|
+ )
|
29
33
|
|
30
34
|
scope "/", KantaWeb do
|
31
35
|
scope "/dashboard", Dashboard do
|
32
36
|
live "/", DashboardLive, :index, route_opts
|
33
37
|
end
|
34
38
|
|
39
|
+ scope "/application_sources", Translations do
|
40
|
+ live "/", ApplicationSourcesLive, :index, route_opts
|
41
|
+ live "/new", ApplicationSourceFormLive, :index, route_opts
|
42
|
+ live "/:id", ApplicationSourceFormLive, :index, route_opts
|
43
|
+ end
|
44
|
+
|
35
45
|
scope "/contexts", Translations do
|
36
46
|
live "/", ContextsLive, :index, route_opts
|
37
47
|
live "/:id", ContextLive, :index, route_opts
|
|
@@ -57,17 +67,13 @@ defmodule KantaWeb.Router do
|
57
67
|
end
|
58
68
|
end
|
59
69
|
|
60
|
- if Code.ensure_loaded?(Phoenix.VerifiedRoutes) do
|
61
|
- quote do
|
62
|
- unquote(scope)
|
70
|
+ quote do
|
71
|
+ unquote(scope)
|
63
72
|
|
64
|
- unless Module.get_attribute(__MODULE__, :kanta_dashboard_prefix) do
|
65
|
- @kanta_dashboard_prefix Phoenix.Router.scoped_path(__MODULE__, path)
|
66
|
- def __kanta_dashboard_prefix__, do: @kanta_dashboard_prefix
|
67
|
- end
|
73
|
+ unless Module.get_attribute(__MODULE__, :kanta_dashboard_prefix) do
|
74
|
+ @kanta_dashboard_prefix KantaWeb.Router.internal_dashboard_scoped_path(path)
|
75
|
+ def __kanta_dashboard_prefix__, do: @kanta_dashboard_prefix
|
68
76
|
end
|
69
|
- else
|
70
|
- scope
|
71
77
|
end
|
72
78
|
end
|
73
79
|
|
|
@@ -83,6 +89,7 @@ defmodule KantaWeb.Router do
|
83
89
|
pipe_through :kanta_api_pipeline
|
84
90
|
get "/", KantaApiController, :index
|
85
91
|
|
92
|
+ resources "/applications", ApplicationSourcesController, only: [:index, :update]
|
86
93
|
resources "/contexts", ContextsController, only: [:index, :update]
|
87
94
|
resources "/domains", DomainsController, only: [:index, :update]
|
88
95
|
resources "/locales", LocalesController, only: [:index, :update]
|
|
@@ -97,6 +104,34 @@ defmodule KantaWeb.Router do
|
97
104
|
end
|
98
105
|
end
|
99
106
|
|
107
|
+ defmacro internal_dashboard_scoped_path(path) do
|
108
|
+ if Code.ensure_loaded?(Phoenix.VerifiedRoutes) do
|
109
|
+ quote do
|
110
|
+ Phoenix.Router.scoped_path(__MODULE__, unquote(path))
|
111
|
+ end
|
112
|
+ else
|
113
|
+ quote do
|
114
|
+ __MODULE__
|
115
|
+ |> Module.get_attribute(:phoenix_top_scopes)
|
116
|
+ |> Map.fetch!(:path)
|
117
|
+ |> KantaWeb.Router.append_last_path(unquote(path))
|
118
|
+ |> Enum.join("/")
|
119
|
+ |> String.replace_prefix("", "/")
|
120
|
+ end
|
121
|
+ end
|
122
|
+ end
|
123
|
+
|
124
|
+ @spec append_last_path(list(), binary()) :: list()
|
125
|
+ def append_last_path(paths, "/" <> path), do: append_last_path(paths, path)
|
126
|
+
|
127
|
+ def append_last_path(paths, path) do
|
128
|
+ if List.last(paths) == path do
|
129
|
+ paths
|
130
|
+ else
|
131
|
+ paths ++ [path]
|
132
|
+ end
|
133
|
+ end
|
134
|
+
|
100
135
|
defp expand_alias({:__aliases__, _, _} = alias, env),
|
101
136
|
do: Macro.expand(alias, %{env | function: {:kanta_dashboard, 2}})
|
changed
lib/kanta_web/templates/layouts/dashboard.html.heex
|
@@ -39,26 +39,30 @@
|
39
39
|
<div class="flex-1 flex flex-col pt-5 pb-4 overflow-y-auto">
|
40
40
|
<div class="flex justify-start items-center mb-4">
|
41
41
|
<Logo.render class="ml-4 w-10 h-10 text-primary-dark dark:text-accent-light" />
|
42
|
- <div class="ml-4 text-4xl text-primary-dark dark:text-accent-light fotn-medium">Kanta</div>
|
42
|
+ <div class="ml-4 text-4xl text-primary-dark dark:text-accent-light font-medium">Kanta</div>
|
43
43
|
</div>
|
44
44
|
<div class="border-b border-slate-50 opacity-50" />
|
45
45
|
<nav class="mt-5 flex-1 px-2 space-y-3">
|
46
|
- <%= link to: unverified_path(@conn, Kanta.Router, "/kanta/dashboard"), class: "bg-white dark:bg-base-dark transition-all hover:bg-slate-100 dark:hover:bg-stone-800 border-slate-300 dark:border-accent-dark shadow-md text-primary-dark dark:text-accent-dark group flex items-center px-2 py-2 text-sm font-semibold rounded-md" do %>
|
46
|
+ <%= link to: dashboard_path(@conn, "/dashboard"), class: "bg-white dark:bg-base-dark transition-all hover:bg-slate-100 dark:hover:bg-stone-800 border-slate-300 dark:border-accent-dark shadow-md text-primary-dark dark:text-accent-dark group flex items-center px-2 py-2 text-sm font-semibold rounded-md" do %>
|
47
47
|
<Icons.inspect class="mr-3 flex-shrink-0 h-6 w-6 text-primary-dark dark:text-accent-dark" />
|
48
48
|
Dashboard
|
49
49
|
<% end %>
|
50
|
- <%= link to: unverified_path(@conn, Kanta.Router, "/kanta/locales"), class: "bg-white dark:bg-base-dark transition-all hover:bg-slate-100 dark:hover:bg-stone-800 border-slate-300 dark:border-accent-dark shadow-md text-primary-dark dark:text-accent-dark group flex items-center px-2 py-2 text-sm font-semibold rounded-md" do %>
|
50
|
+ <%= link to: dashboard_path(@conn, "/locales"), class: "bg-white dark:bg-base-dark transition-all hover:bg-slate-100 dark:hover:bg-stone-800 border-slate-300 dark:border-accent-dark shadow-md text-primary-dark dark:text-accent-dark group flex items-center px-2 py-2 text-sm font-semibold rounded-md" do %>
|
51
51
|
<Icons.languages class="mr-3 flex-shrink-0 h-6 w-6 text-primary-dark dark:text-accent-dark" />
|
52
52
|
Locales
|
53
53
|
<% end %>
|
54
|
- <%= link to: unverified_path(@conn, Kanta.Router, "/kanta/domains"), class: "bg-white dark:bg-base-dark transition-all hover:bg-slate-100 dark:hover:bg-stone-800 border-slate-300 dark:border-accent-dark shadow-md text-primary-dark dark:text-accent-dark group flex items-center px-2 py-2 text-sm font-semibold rounded-md" do %>
|
54
|
+ <%= link to: dashboard_path(@conn, "/domains"), class: "bg-white dark:bg-base-dark transition-all hover:bg-slate-100 dark:hover:bg-stone-800 border-slate-300 dark:border-accent-dark shadow-md text-primary-dark dark:text-accent-dark group flex items-center px-2 py-2 text-sm font-semibold rounded-md" do %>
|
55
55
|
<Icons.album class="mr-3 flex-shrink-0 h-6 w-6 text-primary-dark dark:text-accent-dark" />
|
56
56
|
Domains
|
57
57
|
<% end %>
|
58
|
- <%= link to: unverified_path(@conn, Kanta.Router, "/kanta/contexts"), class: "bg-white dark:bg-base-dark transition-all hover:bg-slate-100 dark:hover:bg-stone-800 border-slate-300 dark:border-accent-dark shadow-md text-primary-dark dark:text-accent-dark group flex items-center px-2 py-2 text-sm font-semibold rounded-md" do %>
|
58
|
+ <%= link to: dashboard_path(@conn, "/contexts"), class: "bg-white dark:bg-base-dark transition-all hover:bg-slate-100 dark:hover:bg-stone-800 border-slate-300 dark:border-accent-dark shadow-md text-primary-dark dark:text-accent-dark group flex items-center px-2 py-2 text-sm font-semibold rounded-md" do %>
|
59
59
|
<Icons.box class="mr-3 flex-shrink-0 h-6 w-6 text-primary-dark dark:text-accent-dark" />
|
60
60
|
Contexts
|
61
61
|
<% end %>
|
62
|
+ <%= link to: dashboard_path(@conn, "/application_sources"), class: "bg-white dark:bg-base-dark transition-all hover:bg-slate-100 dark:hover:bg-stone-800 border-slate-300 dark:border-accent-dark shadow-md text-primary-dark dark:text-accent-dark group flex items-center px-2 py-2 text-sm font-semibold rounded-md" do %>
|
63
|
+ <Icons.computer class="mr-3 flex-shrink-0 h-6 w-6 text-primary-dark dark:text-accent-dark" />
|
64
|
+ Applications
|
65
|
+ <% end %>
|
62
66
|
</nav>
|
63
67
|
</div>
|
64
68
|
|
|
@@ -79,7 +83,7 @@
|
79
83
|
</div>
|
80
84
|
</div>
|
81
85
|
</div>
|
82
|
- <div x-data={"{ open: false}"} class="relative flex flex-col w-0 flex-1 overflow-hidden">
|
86
|
+ <div x-data={"{ open: false }"} class="relative flex flex-col w-0 flex-1 overflow-hidden">
|
83
87
|
<div class="md:hidden pl-1 pt-1 sm:pl-3 sm:pt-3">
|
84
88
|
<button @click="open = !open" class="-ml-0.5 -mt-0.5 h-12 w-12 inline-flex items-center justify-center rounded-md text-slate-500 dark:text-content-light hover:text-primary-dark hover:dark:text-accent-dark focus:outline-none focus:ring-2 focus:ring-inset focus:ring-primary focus:dark:ring-accent-dark">
|
85
89
|
<span class="sr-only">Open sidebar</span>
|
|
@@ -103,19 +107,19 @@
|
103
107
|
</div>
|
104
108
|
<div class="border-b border-slate-50 opacity-50" />
|
105
109
|
<nav class="mt-5 flex-1 px-2 pb-2 space-y-3">
|
106
|
- <%= link to: unverified_path(@conn, Kanta.Router, "/kanta/dashboard"), class: "bg-white dark:bg-base-dark transition-all hover:bg-slate-100 dark:hover:bg-stone-800 border-slate-300 dark:border-accent-dark shadow-md text-primary-dark dark:text-accent-dark group flex items-center px-2 py-2 text-sm font-semibold rounded-md" do %>
|
110
|
+ <%= link to: dashboard_path(@conn, "/dashboard"), class: "bg-white dark:bg-base-dark transition-all hover:bg-slate-100 dark:hover:bg-stone-800 border-slate-300 dark:border-accent-dark shadow-md text-primary-dark dark:text-accent-dark group flex items-center px-2 py-2 text-sm font-semibold rounded-md" do %>
|
107
111
|
<Icons.inspect class="mr-3 flex-shrink-0 h-6 w-6 text-primary-dark dark:text-accent-dark" />
|
108
112
|
Dashboard
|
109
113
|
<% end %>
|
110
|
- <%= link to: unverified_path(@conn, Kanta.Router, "/kanta/locales"), class: "bg-white dark:bg-base-dark transition-all hover:bg-slate-100 dark:hover:bg-stone-800 border-slate-300 dark:border-accent-dark shadow-md text-primary-dark dark:text-accent-dark group flex items-center px-2 py-2 text-sm font-semibold rounded-md" do %>
|
114
|
+ <%= link to: dashboard_path(@conn, "/locales"), class: "bg-white dark:bg-base-dark transition-all hover:bg-slate-100 dark:hover:bg-stone-800 border-slate-300 dark:border-accent-dark shadow-md text-primary-dark dark:text-accent-dark group flex items-center px-2 py-2 text-sm font-semibold rounded-md" do %>
|
111
115
|
<Icons.languages class="mr-3 flex-shrink-0 h-6 w-6 text-primary-dark dark:text-accent-dark" />
|
112
116
|
Locales
|
113
117
|
<% end %>
|
114
|
- <%= link to: unverified_path(@conn, Kanta.Router, "/kanta/domains"), class: "bg-white dark:bg-base-dark transition-all hover:bg-slate-100 dark:hover:bg-stone-800 border-slate-300 dark:border-accent-dark shadow-md text-primary-dark dark:text-accent-dark group flex items-center px-2 py-2 text-sm font-semibold rounded-md" do %>
|
118
|
+ <%= link to: dashboard_path(@conn, "/domains"), class: "bg-white dark:bg-base-dark transition-all hover:bg-slate-100 dark:hover:bg-stone-800 border-slate-300 dark:border-accent-dark shadow-md text-primary-dark dark:text-accent-dark group flex items-center px-2 py-2 text-sm font-semibold rounded-md" do %>
|
115
119
|
<Icons.album class="mr-3 flex-shrink-0 h-6 w-6 text-primary-dark dark:text-accent-dark" />
|
116
120
|
Domains
|
117
121
|
<% end %>
|
118
|
- <%= link to: unverified_path(@conn, Kanta.Router, "/kanta/contexts"), class: "bg-white dark:bg-base-dark transition-all hover:bg-slate-100 dark:hover:bg-stone-800 border-slate-300 dark:border-accent-dark shadow-md text-primary-dark dark:text-accent-dark group flex items-center px-2 py-2 text-sm font-semibold rounded-md" do %>
|
122
|
+ <%= link to: dashboard_path(@conn, "/contexts"), class: "bg-white dark:bg-base-dark transition-all hover:bg-slate-100 dark:hover:bg-stone-800 border-slate-300 dark:border-accent-dark shadow-md text-primary-dark dark:text-accent-dark group flex items-center px-2 py-2 text-sm font-semibold rounded-md" do %>
|
119
123
|
<Icons.box class="mr-3 flex-shrink-0 h-6 w-6 text-primary-dark dark:text-accent-dark" />
|
120
124
|
Contexts
|
121
125
|
<% end %>
|
changed
lib/kanta_web/views/layout_view.ex
|
@@ -21,7 +21,7 @@ defmodule KantaWeb.LayoutView do
|
21
21
|
def asset_path(conn, asset) when asset in [:css, :js] do
|
22
22
|
hash = KantaWeb.Assets.current_hash(asset)
|
23
23
|
|
24
|
- prefix = conn.private.phoenix_router.__kanta_dashboard_prefix__()
|
24
|
+ prefix = dashboard_path(conn)
|
25
25
|
|
26
26
|
Phoenix.VerifiedRoutes.unverified_path(
|
27
27
|
conn,
|
changed
mix.exs
|
@@ -6,7 +6,7 @@ defmodule Kanta.MixProject do
|
6
6
|
app: :kanta,
|
7
7
|
description: "User-friendly translations manager for Elixir/Phoenix projects.",
|
8
8
|
package: package(),
|
9
|
- version: "0.3.1",
|
9
|
+ version: "0.4.0",
|
10
10
|
elixir: "~> 1.14",
|
11
11
|
elixirc_options: [
|
12
12
|
warnings_as_errors: true
|
|
@@ -35,25 +35,29 @@ defmodule Kanta.MixProject do
|
35
35
|
defp deps do
|
36
36
|
[
|
37
37
|
{:expo, "~> 0.3"},
|
38
|
- {:ecto, "~> 3.10"},
|
39
|
- {:ecto_sql, "~> 3.10"},
|
38
|
+ {:ecto, "~> 3.12"},
|
39
|
+ {:ecto_sql, "~> 3.12"},
|
40
40
|
{:phoenix, "~> 1.7.0"},
|
41
41
|
{:phoenix_view, "~> 2.0"},
|
42
42
|
{:phoenix_live_view, "~> 0.20"},
|
43
|
+ {:phoenix_html, "~> 4.0"},
|
44
|
+ {:phoenix_html_helpers, "~> 1.0"},
|
43
45
|
{:tailwind, "~> 0.2", runtime: Mix.env() == :dev},
|
44
46
|
{:jason, "~> 1.0"},
|
45
47
|
{:nebulex, "~> 2.5"},
|
46
48
|
{:shards, "~> 1.0"},
|
47
49
|
{:scrivener, "~> 2.0"},
|
48
50
|
{:scrivener_ecto, "~> 2.0"},
|
49
|
- {:uri_query, "~> 0.1.1"},
|
51
|
+ {:uri_query, "~> 0.2"},
|
50
52
|
# DEV
|
53
|
+ {:versioce, "~> 2.0.0"},
|
54
|
+ {:git_cli, "~> 0.3.0"},
|
51
55
|
{:esbuild, "~> 0.7", only: :dev},
|
52
|
- {:credo, "~> 1.7", only: [:dev, :test], runtime: false},
|
56
|
+ {:credo, "~> 1.7.7", only: [:dev, :test], runtime: false},
|
53
57
|
{:mix_audit, "~> 2.0", only: [:dev, :test], runtime: false},
|
54
58
|
{:gettext, github: "ravensiris/gettext", branch: "runtime-gettext", only: [:dev, :test]},
|
55
59
|
{:ex_doc, "~> 0.27", only: :dev, runtime: false},
|
56
|
- {:dialyxir, "~> 1.3", only: :dev, runtime: false}
|
60
|
+ {:dialyxir, "~> 1.4", only: :dev, runtime: false}
|
57
61
|
]
|
58
62
|
end
|