iron-cms 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -0
  3. data/app/assets/builds/iron.css +68 -24
  4. data/app/assets/tailwind/iron/components/form.css +1 -1
  5. data/app/assets/tailwind/iron/components/sidebar.css +7 -7
  6. data/app/controllers/iron/account/exports_controller.rb +1 -1
  7. data/app/controllers/iron/account/imports_controller.rb +1 -1
  8. data/app/controllers/iron/content_types_controller.rb +3 -3
  9. data/app/controllers/iron/entries_controller.rb +9 -9
  10. data/app/controllers/iron/passwords_controller.rb +5 -5
  11. data/app/controllers/iron/settings_controller.rb +1 -1
  12. data/app/controllers/iron/users/passwords_controller.rb +1 -1
  13. data/app/controllers/iron/users_controller.rb +1 -1
  14. data/app/javascript/iron/controllers/form_controller.js +4 -1
  15. data/app/mailers/iron/application_mailer.rb +1 -1
  16. data/app/mailers/iron/passwords_mailer.rb +1 -1
  17. data/app/models/iron/entry/deep_validation.rb +1 -1
  18. data/app/models/iron/entry/web_publishable.rb +2 -2
  19. data/app/models/iron/user/role.rb +1 -1
  20. data/app/models/iron/user.rb +3 -3
  21. data/app/views/iron/account/exports/index.html.erb +12 -12
  22. data/app/views/iron/account/exports/new.html.erb +11 -11
  23. data/app/views/iron/account/exports/show.html.erb +11 -11
  24. data/app/views/iron/account/imports/index.html.erb +12 -12
  25. data/app/views/iron/account/imports/new.html.erb +13 -13
  26. data/app/views/iron/account/imports/show.html.erb +10 -10
  27. data/app/views/iron/block_definitions/_form.html.erb +2 -1
  28. data/app/views/iron/content_types/_content_type.html.erb +1 -1
  29. data/app/views/iron/content_types/_empty_state.html.erb +3 -3
  30. data/app/views/iron/content_types/_form.html.erb +19 -18
  31. data/app/views/iron/content_types/edit.html.erb +5 -5
  32. data/app/views/iron/content_types/index.html.erb +5 -5
  33. data/app/views/iron/content_types/new.html.erb +3 -3
  34. data/app/views/iron/content_types/show.html.erb +8 -8
  35. data/app/views/iron/entries/_empty_state.html.erb +3 -3
  36. data/app/views/iron/entries/_entry.html.erb +1 -1
  37. data/app/views/iron/entries/_form.html.erb +2 -1
  38. data/app/views/iron/entries/_web_page_settings.html.erb +8 -8
  39. data/app/views/iron/entries/edit.html.erb +5 -5
  40. data/app/views/iron/entries/entry.html.erb +1 -1
  41. data/app/views/iron/entries/index.html.erb +3 -3
  42. data/app/views/iron/entries/new.html.erb +3 -3
  43. data/app/views/iron/entries/search.html.erb +1 -1
  44. data/app/views/iron/home/_administration.html.erb +12 -12
  45. data/app/views/iron/home/_content_types.html.erb +7 -7
  46. data/app/views/iron/home/_greeting.html.erb +1 -1
  47. data/app/views/iron/home/_recent_activity.html.erb +3 -3
  48. data/app/views/iron/home/show.html.erb +1 -1
  49. data/app/views/iron/locales/_locale.html.erb +1 -1
  50. data/app/views/iron/locales/edit.html.erb +5 -5
  51. data/app/views/iron/locales/index.html.erb +7 -7
  52. data/app/views/iron/locales/new.html.erb +3 -3
  53. data/app/views/iron/passwords/edit.html.erb +4 -4
  54. data/app/views/iron/passwords/new.html.erb +5 -5
  55. data/app/views/iron/passwords_mailer/reset.html.erb +1 -2
  56. data/app/views/iron/passwords_mailer/reset.text.erb +1 -2
  57. data/app/views/iron/sessions/new.html.erb +29 -26
  58. data/app/views/iron/sessions/transfers/show.html.erb +4 -4
  59. data/app/views/iron/settings/_change_default_locale_dialog.html.erb +4 -4
  60. data/app/views/iron/settings/_change_name_dialog.html.erb +4 -4
  61. data/app/views/iron/settings/show.html.erb +12 -12
  62. data/app/views/iron/shared/_icon_picker.html.erb +3 -3
  63. data/app/views/iron/shared/_save_bar.html.erb +3 -3
  64. data/app/views/iron/shared/_select.html.erb +1 -1
  65. data/app/views/iron/users/_change_email_dialog.html.erb +5 -5
  66. data/app/views/iron/users/_change_language_dialog.html.erb +6 -6
  67. data/app/views/iron/users/_change_password_dialog.html.erb +6 -6
  68. data/app/views/iron/users/_change_role_dialog.html.erb +5 -5
  69. data/app/views/iron/users/_invite.html.erb +8 -8
  70. data/app/views/iron/users/_transfer.html.erb +12 -12
  71. data/app/views/iron/users/_user.html.erb +2 -2
  72. data/app/views/iron/users/index.html.erb +5 -5
  73. data/app/views/iron/users/new.html.erb +6 -6
  74. data/app/views/iron/users/show.html.erb +27 -27
  75. data/app/views/layouts/iron/_mobile_header.html.erb +3 -3
  76. data/app/views/layouts/iron/_sidebar.html.erb +3 -3
  77. data/app/views/layouts/iron/_sidebar_content.html.erb +14 -14
  78. data/app/views/layouts/iron/_user_menu.html.erb +3 -3
  79. data/app/views/layouts/iron/application.html.erb +2 -2
  80. data/app/views/layouts/iron/authentication.html.erb +4 -4
  81. data/config/locales/en.yml +407 -1
  82. data/config/locales/it.yml +446 -1
  83. data/lib/iron/engine.rb +1 -1
  84. data/lib/iron/version.rb +1 -1
  85. data/lib/iron.rb +1 -1
  86. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7cf5dcee3aab1c40252f69f9314a3f3db2c43b359bd7045264c837856406e260
4
- data.tar.gz: 5e0e7f58dfa1630254637185f8e9adce63800fc83b4633a805aae36ac130e742
3
+ metadata.gz: e4875e891f39ab409c8460006c443ceae03bef9cb9cbd48ea8a0b5603d090405
4
+ data.tar.gz: 4a45587b457c4aff648bc7d61d714485ac26682533cdb1c9016d00b27a1c6fab
5
5
  SHA512:
6
- metadata.gz: ff9f0219866d562528218a0aa7156947e6c5b813ed1c2faf2142122ff430e23b2bf6e5d1c267c1d3db0ef4d566444ea253c43a0792f4422f944064554112971c
7
- data.tar.gz: 186733969e7fdd9f69b1f7ef38391b7985165d29ede268eac7288a4b3f20c0277c39500cbdce85a860fa9a1c7d2c7ddca4cbbe11c42cae0f5de621ad62799139
6
+ metadata.gz: 9ddcc960d6c7572dcd8d459c883d522d1f25b5f33ef23f75cc7cd1a444e1192864078d2c573bf2f868858582a5316715008314dbe8be3f2acaf68d18f5a8ba6f
7
+ data.tar.gz: 299f14dad319f969252baef1b8cc09047b8c6e2a832cde32302c028b6f1dcb51ff562d49c760a3c071d77d654f5718772ef97e6c434c83beef6bd7bbffb0f725
data/README.md CHANGED
@@ -175,6 +175,13 @@ Then copy iron migrations:
175
175
  bin/rails iron:install:migrations
176
176
  ```
177
177
 
178
+ Configure the mailer sender address used for system emails (e.g. password resets):
179
+
180
+ ```ruby
181
+ # config/initializers/iron.rb
182
+ Iron.mailer_sender = "hello@mysite.com"
183
+ ```
184
+
178
185
  ## Architecture
179
186
 
180
187
  Here is an overview of how the CMS is structured.
@@ -1020,6 +1020,7 @@
1020
1020
  .sidebar-item {
1021
1021
  position: relative;
1022
1022
  display: flex;
1023
+ align-items: center;
1023
1024
  column-gap: calc(var(--spacing) * 3);
1024
1025
  border-radius: var(--radius-lg);
1025
1026
  padding: calc(var(--spacing) * 2);
@@ -1032,15 +1033,15 @@
1032
1033
  transition-duration: var(--tw-duration, var(--default-transition-duration));
1033
1034
  --tw-duration: 150ms;
1034
1035
  transition-duration: 150ms;
1035
- color: var(--color-stone-600);
1036
+ color: var(--color-stone-700);
1036
1037
  @media (prefers-color-scheme: dark) {
1037
1038
  color: var(--color-stone-400);
1038
1039
  }
1039
1040
  &:hover {
1040
1041
  @media (hover: hover) {
1041
- background-color: color-mix(in srgb, oklch(97% 0.001 106.424) 70%, transparent);
1042
+ background-color: color-mix(in srgb, oklch(86.9% 0.005 56.366) 40%, transparent);
1042
1043
  @supports (color: color-mix(in lab, red, red)) {
1043
- background-color: color-mix(in oklab, var(--color-stone-100) 70%, transparent);
1044
+ background-color: color-mix(in oklab, var(--color-stone-300) 40%, transparent);
1044
1045
  }
1045
1046
  }
1046
1047
  }
@@ -1079,7 +1080,7 @@
1079
1080
  }
1080
1081
  :is(& > *) {
1081
1082
  &[data-slot="icon"] {
1082
- color: var(--color-stone-400);
1083
+ color: var(--color-stone-500);
1083
1084
  }
1084
1085
  }
1085
1086
  @media (prefers-color-scheme: dark) {
@@ -1110,9 +1111,9 @@
1110
1111
  }
1111
1112
  }
1112
1113
  &[data-current] {
1113
- background-color: color-mix(in srgb, oklch(97% 0.001 106.424) 70%, transparent);
1114
+ background-color: color-mix(in srgb, oklch(86.9% 0.005 56.366) 50%, transparent);
1114
1115
  @supports (color: color-mix(in lab, red, red)) {
1115
- background-color: color-mix(in oklab, var(--color-stone-100) 70%, transparent);
1116
+ background-color: color-mix(in oklab, var(--color-stone-300) 50%, transparent);
1116
1117
  }
1117
1118
  }
1118
1119
  &[data-current] {
@@ -1468,12 +1469,21 @@
1468
1469
  .z-50 {
1469
1470
  z-index: 50;
1470
1471
  }
1472
+ .col-span-2 {
1473
+ grid-column: span 2 / span 2;
1474
+ }
1471
1475
  .col-start-1 {
1472
1476
  grid-column-start: 1;
1473
1477
  }
1478
+ .col-start-2 {
1479
+ grid-column-start: 2;
1480
+ }
1474
1481
  .row-start-1 {
1475
1482
  grid-row-start: 1;
1476
1483
  }
1484
+ .row-start-2 {
1485
+ grid-row-start: 2;
1486
+ }
1477
1487
  .container {
1478
1488
  width: 100%;
1479
1489
  @media (width >= 40rem) {
@@ -1981,6 +1991,9 @@
1981
1991
  .mt-1 {
1982
1992
  margin-top: calc(var(--spacing) * 1);
1983
1993
  }
1994
+ .mt-1\.5 {
1995
+ margin-top: calc(var(--spacing) * 1.5);
1996
+ }
1984
1997
  .mt-2 {
1985
1998
  margin-top: calc(var(--spacing) * 2);
1986
1999
  }
@@ -1990,6 +2003,9 @@
1990
2003
  .mt-4 {
1991
2004
  margin-top: calc(var(--spacing) * 4);
1992
2005
  }
2006
+ .mt-5 {
2007
+ margin-top: calc(var(--spacing) * 5);
2008
+ }
1993
2009
  .mt-6 {
1994
2010
  margin-top: calc(var(--spacing) * 6);
1995
2011
  }
@@ -2275,6 +2291,9 @@
2275
2291
  .w-10 {
2276
2292
  width: calc(var(--spacing) * 10);
2277
2293
  }
2294
+ .w-12 {
2295
+ width: calc(var(--spacing) * 12);
2296
+ }
2278
2297
  .w-16 {
2279
2298
  width: calc(var(--spacing) * 16);
2280
2299
  }
@@ -2396,6 +2415,9 @@
2396
2415
  .grid-cols-8 {
2397
2416
  grid-template-columns: repeat(8, minmax(0, 1fr));
2398
2417
  }
2418
+ .grid-cols-\[1fr_auto\] {
2419
+ grid-template-columns: 1fr auto;
2420
+ }
2399
2421
  .flex-col {
2400
2422
  flex-direction: column;
2401
2423
  }
@@ -2539,6 +2561,9 @@
2539
2561
  .self-center {
2540
2562
  align-self: center;
2541
2563
  }
2564
+ .self-end {
2565
+ align-self: flex-end;
2566
+ }
2542
2567
  .self-start {
2543
2568
  align-self: flex-start;
2544
2569
  }
@@ -2625,6 +2650,12 @@
2625
2650
  .border-stone-300 {
2626
2651
  border-color: var(--color-stone-300);
2627
2652
  }
2653
+ .border-stone-300\/50 {
2654
+ border-color: color-mix(in srgb, oklch(86.9% 0.005 56.366) 50%, transparent);
2655
+ @supports (color: color-mix(in lab, red, red)) {
2656
+ border-color: color-mix(in oklab, var(--color-stone-300) 50%, transparent);
2657
+ }
2658
+ }
2628
2659
  .border-stone-700 {
2629
2660
  border-color: var(--color-stone-700);
2630
2661
  }
@@ -2948,15 +2979,15 @@
2948
2979
  .bg-stone-50 {
2949
2980
  background-color: var(--color-stone-50);
2950
2981
  }
2951
- .bg-stone-50\/50 {
2952
- background-color: color-mix(in srgb, oklch(98.5% 0.001 106.423) 50%, transparent);
2953
- @supports (color: color-mix(in lab, red, red)) {
2954
- background-color: color-mix(in oklab, var(--color-stone-50) 50%, transparent);
2955
- }
2956
- }
2957
2982
  .bg-stone-100 {
2958
2983
  background-color: var(--color-stone-100);
2959
2984
  }
2985
+ .bg-stone-200\/50 {
2986
+ background-color: color-mix(in srgb, oklch(92.3% 0.003 48.717) 50%, transparent);
2987
+ @supports (color: color-mix(in lab, red, red)) {
2988
+ background-color: color-mix(in oklab, var(--color-stone-200) 50%, transparent);
2989
+ }
2990
+ }
2960
2991
  .bg-stone-800 {
2961
2992
  background-color: var(--color-stone-800);
2962
2993
  }
@@ -3011,6 +3042,11 @@
3011
3042
  --tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);
3012
3043
  --tw-gradient-stops: var(--tw-gradient-via-stops);
3013
3044
  }
3045
+ .via-stone-300 {
3046
+ --tw-gradient-via: var(--color-stone-300);
3047
+ --tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);
3048
+ --tw-gradient-stops: var(--tw-gradient-via-stops);
3049
+ }
3014
3050
  .to-indigo-50 {
3015
3051
  --tw-gradient-to: var(--color-indigo-50);
3016
3052
  --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
@@ -3116,6 +3152,9 @@
3116
3152
  .py-16 {
3117
3153
  padding-block: calc(var(--spacing) * 16);
3118
3154
  }
3155
+ .pt-1 {
3156
+ padding-top: calc(var(--spacing) * 1);
3157
+ }
3119
3158
  .pt-4 {
3120
3159
  padding-top: calc(var(--spacing) * 4);
3121
3160
  }
@@ -3252,6 +3291,10 @@
3252
3291
  font-size: var(--text-lg);
3253
3292
  line-height: var(--tw-leading, var(--text-lg--line-height));
3254
3293
  }
3294
+ .text-lg\/none {
3295
+ font-size: var(--text-lg);
3296
+ line-height: 1;
3297
+ }
3255
3298
  .text-sm {
3256
3299
  font-size: var(--text-sm);
3257
3300
  line-height: var(--tw-leading, var(--text-sm--line-height));
@@ -3334,12 +3377,6 @@
3334
3377
  .text-stone-400 {
3335
3378
  color: var(--color-stone-400);
3336
3379
  }
3337
- .text-stone-400\/70 {
3338
- color: color-mix(in srgb, oklch(70.9% 0.01 56.259) 70%, transparent);
3339
- @supports (color: color-mix(in lab, red, red)) {
3340
- color: color-mix(in oklab, var(--color-stone-400) 70%, transparent);
3341
- }
3342
- }
3343
3380
  .text-stone-500 {
3344
3381
  color: var(--color-stone-500);
3345
3382
  }
@@ -3756,12 +3793,12 @@
3756
3793
  }
3757
3794
  }
3758
3795
  }
3759
- .hover\:bg-stone-100\/70 {
3796
+ .hover\:bg-stone-300\/40 {
3760
3797
  &:hover {
3761
3798
  @media (hover: hover) {
3762
- background-color: color-mix(in srgb, oklch(97% 0.001 106.424) 70%, transparent);
3799
+ background-color: color-mix(in srgb, oklch(86.9% 0.005 56.366) 40%, transparent);
3763
3800
  @supports (color: color-mix(in lab, red, red)) {
3764
- background-color: color-mix(in oklab, var(--color-stone-100) 70%, transparent);
3801
+ background-color: color-mix(in oklab, var(--color-stone-300) 40%, transparent);
3765
3802
  }
3766
3803
  }
3767
3804
  }
@@ -3787,6 +3824,13 @@
3787
3824
  }
3788
3825
  }
3789
3826
  }
3827
+ .hover\:text-stone-600 {
3828
+ &:hover {
3829
+ @media (hover: hover) {
3830
+ color: var(--color-stone-600);
3831
+ }
3832
+ }
3833
+ }
3790
3834
  .hover\:text-stone-700 {
3791
3835
  &:hover {
3792
3836
  @media (hover: hover) {
@@ -4803,16 +4847,16 @@
4803
4847
  }
4804
4848
  }
4805
4849
  }
4806
- .sidebar-monogram {
4850
+ .monogram {
4807
4851
  background: radial-gradient(circle at 30% 30%, var(--color-stone-300), transparent 60%), radial-gradient(circle at 70% 70%, var(--color-stone-600), transparent 60%), var(--color-stone-400);
4808
4852
  }
4809
4853
  @media (prefers-color-scheme: dark) {
4810
- .sidebar-monogram {
4854
+ .monogram {
4811
4855
  background: radial-gradient(circle at 30% 30%, var(--color-stone-500), transparent 60%), radial-gradient(circle at 70% 70%, var(--color-stone-800), transparent 60%), var(--color-stone-600);
4812
4856
  }
4813
4857
  }
4814
4858
  @layer base {
4815
- form {
4859
+ form:not(.auth-form) {
4816
4860
  textarea {
4817
4861
  position: relative;
4818
4862
  display: block;
@@ -1,5 +1,5 @@
1
1
  @layer base {
2
- form {
2
+ form:not(.auth-form) {
3
3
  textarea {
4
4
  @apply textarea;
5
5
  }
@@ -1,4 +1,4 @@
1
- .sidebar-monogram {
1
+ .monogram {
2
2
  background:
3
3
  radial-gradient(circle at 30% 30%, var(--color-stone-300), transparent 60%),
4
4
  radial-gradient(circle at 70% 70%, var(--color-stone-600), transparent 60%),
@@ -6,7 +6,7 @@
6
6
  }
7
7
 
8
8
  @media (prefers-color-scheme: dark) {
9
- .sidebar-monogram {
9
+ .monogram {
10
10
  background:
11
11
  radial-gradient(circle at 30% 30%, var(--color-stone-500), transparent 60%),
12
12
  radial-gradient(circle at 70% 70%, var(--color-stone-800), transparent 60%),
@@ -16,18 +16,18 @@
16
16
 
17
17
  @utility sidebar-item {
18
18
  /* Base */
19
- @apply relative flex gap-x-3 rounded-lg p-2 text-sm/6 font-medium;
19
+ @apply relative flex items-center gap-x-3 rounded-lg p-2 text-sm/6 font-medium;
20
20
  @apply transition-colors duration-150;
21
21
  /* Default state */
22
- @apply text-stone-600 dark:text-stone-400;
22
+ @apply text-stone-700 dark:text-stone-400;
23
23
  /* Hover state */
24
- @apply hover:bg-stone-100/70 hover:text-stone-900 dark:hover:bg-white/5 dark:hover:text-white;
24
+ @apply hover:bg-stone-300/40 hover:text-stone-900 dark:hover:bg-white/5 dark:hover:text-white;
25
25
  /* Leading icon */
26
26
  @apply *:data-[slot=icon]:size-5 *:data-[slot=icon]:shrink-0;
27
- @apply *:data-[slot=icon]:text-stone-400 dark:*:data-[slot=icon]:text-stone-500;
27
+ @apply *:data-[slot=icon]:text-stone-500 dark:*:data-[slot=icon]:text-stone-500;
28
28
  @apply hover:*:data-[slot=icon]:text-stone-600 dark:hover:*:data-[slot=icon]:text-stone-300;
29
29
  /* Current state */
30
- @apply data-current:bg-stone-100/70 data-current:text-stone-900;
30
+ @apply data-current:bg-stone-300/50 data-current:text-stone-900;
31
31
  @apply dark:data-current:bg-white/5 dark:data-current:text-white;
32
32
  @apply data-current:*:data-[slot=icon]:text-stone-700;
33
33
  @apply dark:data-current:*:data-[slot=icon]:text-white;
@@ -11,7 +11,7 @@ module Iron
11
11
  def create
12
12
  @export = Account::Export.create!(export_params)
13
13
  @export.process_later
14
- redirect_to @export, notice: "Export started"
14
+ redirect_to @export, notice: t("iron.account.exports.notifications.started")
15
15
  end
16
16
 
17
17
  def show
@@ -12,7 +12,7 @@ module Iron
12
12
  @import = Account::Import.create!(import_params)
13
13
  @import.file.attach(params[:account_import][:file])
14
14
  @import.process_later
15
- redirect_to @import, notice: "Import started"
15
+ redirect_to @import, notice: t("iron.account.imports.notifications.started")
16
16
  end
17
17
 
18
18
  def show
@@ -22,7 +22,7 @@ module Iron
22
22
  @content_type = ContentType.new(content_type_params)
23
23
 
24
24
  if @content_type.save
25
- redirect_to @content_type, notice: "Content type was successfully created."
25
+ redirect_to @content_type, notice: t("iron.content_types.notifications.created")
26
26
  else
27
27
  render :new, status: :unprocessable_entity
28
28
  end
@@ -30,7 +30,7 @@ module Iron
30
30
 
31
31
  def update
32
32
  if @content_type.update(content_type_params)
33
- redirect_to @content_type, notice: "Content type was successfully updated.", status: :see_other
33
+ redirect_to @content_type, notice: t("iron.content_types.notifications.updated"), status: :see_other
34
34
  else
35
35
  render :edit, status: :unprocessable_entity
36
36
  end
@@ -38,7 +38,7 @@ module Iron
38
38
 
39
39
  def destroy
40
40
  @content_type.destroy!
41
- redirect_to content_types_path, notice: "Content type was successfully destroyed.", status: :see_other
41
+ redirect_to content_types_path, notice: t("iron.content_types.notifications.destroyed"), status: :see_other
42
42
  end
43
43
 
44
44
  private
@@ -40,15 +40,15 @@ module Iron
40
40
 
41
41
  if @entry.save
42
42
  if @entry.content_type.single?
43
- redirect_to entry_content_type_url(@entry.content_type), notice: "#{@entry.content_type.name} was successfully created."
43
+ redirect_to entry_content_type_url(@entry.content_type), notice: t("iron.entries.notifications.created", content_type: @entry.content_type.name)
44
44
  else
45
- redirect_to edit_entry_url(@entry), notice: "#{@entry.content_type.name} was successfully created."
45
+ redirect_to edit_entry_url(@entry), notice: t("iron.entries.notifications.created", content_type: @entry.content_type.name)
46
46
  end
47
47
  else
48
48
  if @entry.errors.any?
49
- flash.now[:alert] = "Could not save changes due to validation errors"
49
+ flash.now[:alert] = t("iron.entries.alerts.validation_errors")
50
50
  else
51
- flash.now[:alert] = "#{@entry.content_type.name} could not be created"
51
+ flash.now[:alert] = t("iron.entries.alerts.create_failed", content_type: @entry.content_type.name)
52
52
  end
53
53
  render :new, status: :unprocessable_entity
54
54
  end
@@ -57,15 +57,15 @@ module Iron
57
57
  def update
58
58
  if @entry.update(entry_params)
59
59
  if @entry.content_type.single?
60
- redirect_to entry_content_type_url(@entry.content_type, locale: @locale.code), notice: "#{@entry.content_type.name} was successfully updated."
60
+ redirect_to entry_content_type_url(@entry.content_type, locale: @locale.code), notice: t("iron.entries.notifications.updated", content_type: @entry.content_type.name)
61
61
  else
62
- redirect_to edit_entry_url(@entry, locale: @locale.code), notice: "#{@entry.content_type.name} was successfully updated."
62
+ redirect_to edit_entry_url(@entry, locale: @locale.code), notice: t("iron.entries.notifications.updated", content_type: @entry.content_type.name)
63
63
  end
64
64
  else
65
65
  if @entry.errors.any?
66
- flash.now[:alert] = "Could not save changes due to validation errors"
66
+ flash.now[:alert] = t("iron.entries.alerts.validation_errors")
67
67
  else
68
- flash.now[:alert] = "#{@entry.content_type.name} could not be updated"
68
+ flash.now[:alert] = t("iron.entries.alerts.update_failed", content_type: @entry.content_type.name)
69
69
  end
70
70
  render :edit, status: :unprocessable_entity
71
71
  end
@@ -73,7 +73,7 @@ module Iron
73
73
 
74
74
  def destroy
75
75
  @entry.destroy!
76
- redirect_to content_type_entries_path(@entry.content_type), notice: "Entry was successfully destroyed.", status: :see_other
76
+ redirect_to content_type_entries_path(@entry.content_type), notice: t("iron.entries.notifications.destroyed"), status: :see_other
77
77
  end
78
78
 
79
79
  private
@@ -13,7 +13,7 @@ module Iron
13
13
  Iron::PasswordsMailer.reset(user).deliver_later
14
14
  end
15
15
 
16
- redirect_to sign_in_url, notice: "Password reset instructions sent."
16
+ redirect_to sign_in_url, notice: t("iron.passwords.notifications.reset_instructions_sent")
17
17
  end
18
18
 
19
19
  def edit
@@ -21,23 +21,23 @@ module Iron
21
21
 
22
22
  def update
23
23
  if @user.update(params.permit(:password, :password_confirmation))
24
- redirect_to sign_in_url, notice: "Password has been reset."
24
+ redirect_to sign_in_url, notice: t("iron.passwords.notifications.reset_success")
25
25
  else
26
- redirect_to edit_password_url(params[:token]), alert: "Passwords did not match."
26
+ redirect_to edit_password_url(params[:token]), alert: t("iron.passwords.alerts.passwords_mismatch")
27
27
  end
28
28
  end
29
29
 
30
30
  private
31
31
  def require_email_configuration
32
32
  unless EmailConfiguration.available?
33
- redirect_to sign_in_url, alert: "Email is not configured. Please contact an administrator for a transfer link."
33
+ redirect_to sign_in_url, alert: t("iron.passwords.alerts.email_not_configured")
34
34
  end
35
35
  end
36
36
 
37
37
  def set_user_by_token
38
38
  @user = User.find_by_password_reset_token!(params[:token])
39
39
  rescue ActiveSupport::MessageVerifier::InvalidSignature
40
- redirect_to new_password_url, alert: "Password reset link is invalid or has expired."
40
+ redirect_to new_password_url, alert: t("iron.passwords.alerts.reset_link_invalid")
41
41
  end
42
42
  end
43
43
  end
@@ -9,7 +9,7 @@ module Iron
9
9
 
10
10
  def update
11
11
  if Current.account.update(account_params)
12
- redirect_back fallback_location: settings_path, notice: "Settings updated successfully."
12
+ redirect_back fallback_location: settings_path, notice: t("iron.settings.notifications.updated")
13
13
  else
14
14
  @account = Current.account
15
15
  @locales = Locale.all
@@ -5,7 +5,7 @@ module Iron
5
5
 
6
6
  def update
7
7
  if @user.update(params.require(:user).permit(:password, :password_confirmation))
8
- redirect_to @user, notice: "Password changed."
8
+ redirect_to @user, notice: t("iron.users.notifications.password_changed")
9
9
  else
10
10
  render "iron/users/show", status: :unprocessable_entity
11
11
  end
@@ -32,7 +32,7 @@ module Iron
32
32
 
33
33
  def destroy
34
34
  if @user.destroy
35
- redirect_back fallback_location: users_path, notice: "User was successfully removed.", status: :see_other
35
+ redirect_back fallback_location: users_path, notice: t("iron.users.notifications.removed"), status: :see_other
36
36
  else
37
37
  redirect_back fallback_location: users_path, alert: @user.errors.full_messages.to_sentence, status: :see_other
38
38
  end
@@ -1,6 +1,9 @@
1
1
  import { Controller } from "@hotwired/stimulus";
2
2
 
3
3
  export default class extends Controller {
4
+ static values = {
5
+ unsavedMessage: { type: String, default: "You have unsaved changes. Are you sure you want to leave?" }
6
+ };
4
7
  // Lifecycle
5
8
 
6
9
  connect() {
@@ -100,7 +103,7 @@ export default class extends Controller {
100
103
 
101
104
  #warnBeforeTurboVisit = (event) => {
102
105
  if (this.#isDirty) {
103
- if (!confirm("You have unsaved changes. Are you sure you want to leave?")) {
106
+ if (!confirm(this.unsavedMessageValue)) {
104
107
  event.preventDefault();
105
108
  }
106
109
  }
@@ -1,6 +1,6 @@
1
1
  module Iron
2
2
  class ApplicationMailer < ActionMailer::Base
3
- default from: "from@example.com"
3
+ default from: -> { Iron.mailer_sender }
4
4
  layout "mailer"
5
5
  end
6
6
  end
@@ -2,7 +2,7 @@ module Iron
2
2
  class PasswordsMailer < ApplicationMailer
3
3
  def reset(user)
4
4
  @user = user
5
- mail subject: "Reset your password", to: user.email_address
5
+ mail subject: t(".subject"), to: user.email_address
6
6
  end
7
7
  end
8
8
  end
@@ -25,7 +25,7 @@ module Iron
25
25
  end
26
26
  end
27
27
  fields.select { |f| f.parent.nil? }.each { |f| walk.call(f) }
28
- errors.add(:base, "One or more fields are invalid") if any_invalid
28
+ errors.add(:base, :fields_invalid) if any_invalid
29
29
  end
30
30
  end
31
31
  end
@@ -7,7 +7,7 @@ module Iron
7
7
  before_validation :generate_route, if: :should_generate_route?
8
8
 
9
9
  validates :route, uniqueness: { scope: :content_type_id, allow_blank: true }, if: :route_required?
10
- validates :route, format: { with: /\A[a-z0-9\-\/]*\z/, message: "can only contain lowercase letters, numbers, hyphens and slashes" }, allow_blank: true
10
+ validates :route, format: { with: /\A[a-z0-9\-\/]*\z/, message: :invalid_route_format }, allow_blank: true
11
11
  validate :index_route_uniqueness, if: :use_as_index
12
12
  end
13
13
 
@@ -78,7 +78,7 @@ module Iron
78
78
 
79
79
  def index_route_uniqueness
80
80
  if content_type.index_entry && content_type.index_entry.id != id
81
- errors.add(:use_as_index, "is already taken - another entry is already set as the index page")
81
+ errors.add(:use_as_index, :already_taken_as_index)
82
82
  end
83
83
  end
84
84
  end
@@ -14,7 +14,7 @@ module Iron::User::Role
14
14
 
15
15
  def cannot_change_own_role
16
16
  if current?
17
- errors.add(:role, "cannot be changed for your own account")
17
+ errors.add(:role, :cannot_change_own_role)
18
18
  self.role = role_was
19
19
  end
20
20
  end
@@ -3,9 +3,9 @@ module Iron
3
3
  include Role
4
4
  include Transferable
5
5
 
6
- ADMIN_LANGUAGES = { "en" => "English", "it" => "Italiano" }.freeze
6
+ ADMIN_LANGUAGES = %w[en it].freeze
7
7
 
8
- validates :language, inclusion: { in: ADMIN_LANGUAGES.keys }
8
+ validates :language, inclusion: { in: ADMIN_LANGUAGES }
9
9
 
10
10
  before_destroy :ensure_not_current_user
11
11
 
@@ -36,7 +36,7 @@ module Iron
36
36
 
37
37
  def ensure_not_current_user
38
38
  if Current.user == self
39
- errors.add(:base, "You cannot delete your own account while logged in")
39
+ errors.add(:base, :cannot_delete_self)
40
40
  throw :abort
41
41
  end
42
42
  end
@@ -1,19 +1,19 @@
1
- <% content_for :title, "Exports" %>
1
+ <% content_for :title, t(".title") %>
2
2
 
3
3
  <div class="page-content space-y-8">
4
4
  <header class="relative pb-8">
5
5
  <div class="flex items-center justify-between mb-6">
6
- <%= link_to "Settings", settings_path, class: "breadcrumb" %>
6
+ <%= link_to t(".breadcrumb"), settings_path, class: "breadcrumb" %>
7
7
 
8
8
  <%= link_to new_account_export_path, class: "button-primary" do %>
9
9
  <%= icon "plus", class: "size-4" %>
10
- New Export
10
+ <%= t(".new_export") %>
11
11
  <% end %>
12
12
  </div>
13
13
 
14
- <h1 class="page-title-lg">Exports</h1>
14
+ <h1 class="page-title-lg"><%= t(".heading") %></h1>
15
15
  <p class="mt-1 text-sm text-stone-500 dark:text-stone-400">
16
- <%= @exports.count %> <%= "export".pluralize(@exports.count) %>
16
+ <%= t(".count", count: @exports.count) %>
17
17
  </p>
18
18
 
19
19
  <div class="absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-stone-200 to-transparent dark:via-stone-700"></div>
@@ -35,16 +35,16 @@
35
35
  <span class="flex-1 min-w-0">
36
36
  <%= link_to export.title, account_export_path(export), class: "card-link block text-sm font-medium text-stone-900 dark:text-white" %>
37
37
  <span class="block text-xs text-stone-500 dark:text-stone-400">
38
- <%= export.include_schema? ? "Schema" : "" %><%= export.include_schema? && export.include_content? ? " + " : "" %><%= export.include_content? ? "Content" : "" %>
38
+ <%= export.include_schema? ? t("iron.shared.data_transfer.schema") : "" %><%= export.include_schema? && export.include_content? ? " + " : "" %><%= export.include_content? ? t("iron.shared.data_transfer.content") : "" %>
39
39
  </span>
40
40
  </span>
41
41
  <span class="flex items-center gap-3">
42
42
  <% if export.pending? || export.processing? %>
43
- <%= badge "Processing" %>
43
+ <%= badge t("iron.shared.data_transfer.processing") %>
44
44
  <% elsif export.completed? %>
45
- <%= badge "Completed", class: "badge-green" %>
45
+ <%= badge t("iron.shared.data_transfer.completed"), class: "badge-green" %>
46
46
  <% elsif export.failed? %>
47
- <%= badge "Failed", class: "badge-red" %>
47
+ <%= badge t("iron.shared.data_transfer.failed"), class: "badge-red" %>
48
48
  <% end %>
49
49
  <%= icon "chevron-right", class: "size-4 text-stone-300 dark:text-stone-600" %>
50
50
  </span>
@@ -57,11 +57,11 @@
57
57
  <div class="flex items-center justify-center size-12 rounded-xl bg-stone-100 dark:bg-stone-800 mb-4">
58
58
  <%= icon "download", class: "size-6 text-stone-400 dark:text-stone-500" %>
59
59
  </div>
60
- <h3 class="text-sm font-medium text-stone-900 dark:text-white mb-1">No exports yet</h3>
61
- <p class="text-sm text-stone-500 dark:text-stone-400 mb-4">Create an export to download a snapshot of your content.</p>
60
+ <h3 class="text-sm font-medium text-stone-900 dark:text-white mb-1"><%= t(".empty_state_title") %></h3>
61
+ <p class="text-sm text-stone-500 dark:text-stone-400 mb-4"><%= t(".empty_state_description") %></p>
62
62
  <%= link_to new_account_export_path, class: "button-primary" do %>
63
63
  <%= icon "plus", class: "size-4" %>
64
- New Export
64
+ <%= t(".new_export") %>
65
65
  <% end %>
66
66
  </div>
67
67
  </div>