Compare commits

...

122 Commits

Author SHA1 Message Date
Tonya
25c76522d6 Merge pull request #185
* feat: changeable items per table page

* Merge branch 'main' into main
2024-09-05 19:57:12 -04:00
Tonya
c0e2aa5c62 Merge pull request #197
* feat: add search to multi select and improve behaviour when multiple …
2024-09-05 19:52:26 -04:00
Weblate
d0b9f742ae Translated using Weblate (Polish)
Currently translated at 88.8% (80 of 90 strings)

Translated using Weblate (Polish)

Currently translated at 88.8% (80 of 90 strings)

Co-authored-by: Bart <bartosz.domanski97@gmail.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/pl/
Translation: Homebox/Frontend
2024-09-05 20:53:24 +00:00
Weblate
80d56829c5 Translated using Weblate (German)
Currently translated at 100.0% (90 of 90 strings)

Translated using Weblate (Polish)

Currently translated at 81.1% (73 of 90 strings)

Translated using Weblate (Polish)

Currently translated at 81.1% (73 of 90 strings)

Translated using Weblate (Catalan)

Currently translated at 48.8% (44 of 90 strings)

Co-authored-by: Bart <bartosz.domanski97@gmail.com>
Co-authored-by: Mats <sysadminsmedia@mats-bueser.de>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Co-authored-by: Xavier Clotet <x.clotetfons@gmail.com>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/ca/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/de/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/pl/
Translation: Homebox/Frontend
2024-09-05 20:52:03 +00:00
Weblate
0946310f60 Added translation using Weblate (Polish)
Co-authored-by: Bart <bartosz.domanski97@gmail.com>
2024-09-05 20:32:16 +00:00
Matt Kilgore
7c855cf55d fix: regional languages not matching correctly 2024-09-05 15:48:00 -04:00
Tonya
0ab95fb670 feat: compare filter values on a unique field instead of by reference for finding unselected (#195) 2024-09-05 15:41:29 -04:00
Tonya
1e81b4bab4 feat: improve loading state for creation and fix types for adding image (#196) 2024-09-05 15:40:57 -04:00
Tonya
67c50068d9 fix: styles on home page (#193) 2024-09-05 14:29:33 -04:00
Weblate
c3628e36f7 Translated using Weblate (French)
Currently translated at 100.0% (90 of 90 strings)

Translated using Weblate (French)

Currently translated at 100.0% (90 of 90 strings)

Translated using Weblate (French)

Currently translated at 100.0% (90 of 90 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (90 of 90 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (90 of 90 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (90 of 90 strings)

Translated using Weblate (Catalan)

Currently translated at 43.3% (39 of 90 strings)

Translated using Weblate (Catalan)

Currently translated at 43.3% (39 of 90 strings)

Translated using Weblate (Slovenian)

Currently translated at 100.0% (90 of 90 strings)

Translated using Weblate (Slovenian)

Currently translated at 100.0% (90 of 90 strings)

Translated using Weblate (German)

Currently translated at 100.0% (90 of 90 strings)

Translated using Weblate (German)

Currently translated at 100.0% (90 of 90 strings)

Co-authored-by: 101br03k <warmerdamm03@gmail.com>
Co-authored-by: Dest.Com <azevedo-a@hotmail.fr>
Co-authored-by: Jackxwb <xwb9606@163.com>
Co-authored-by: Maxklos <herzognikolaus@gmail.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Co-authored-by: Stair <nrjava06@gmail.com>
Co-authored-by: Xavier Clotet <x.clotetfons@gmail.com>
Co-authored-by: thehijacker <thehijacker@gmail.com>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/ca/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/de/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/fr/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/nl/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/sl/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/zh_Hans/
Translation: Homebox/Frontend
2024-09-05 14:20:41 +00:00
Weblate
526799c6da Added translation using Weblate (Catalan)
Co-authored-by: Xavier Clotet <x.clotetfons@gmail.com>
2024-09-05 12:35:43 +00:00
Weblate
4ef7529533 Translated using Weblate (Slovenian)
Currently translated at 37.7% (34 of 90 strings)

Translated using Weblate (Slovenian)

Currently translated at 37.7% (34 of 90 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (90 of 90 strings)

Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Co-authored-by: Slydite4 <39199098+Slydite4@users.noreply.github.com>
Co-authored-by: thehijacker <thehijacker@gmail.com>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/es/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/sl/
Translation: Homebox/Frontend
2024-09-05 04:36:37 +00:00
Weblate
b06d670dff Added translation using Weblate (Slovenian)
Co-authored-by: thehijacker <thehijacker@gmail.com>
2024-09-05 04:29:38 +00:00
Weblate
02c0453ff3 Translated using Weblate (French)
Currently translated at 94.4% (85 of 90 strings)

Co-authored-by: Jean-Philippe Baril <weblate.org@alias.trebaxis.net>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/fr/
Translation: Homebox/Frontend
2024-09-04 18:48:34 +00:00
Weblate
09358aa5b2 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (90 of 90 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (90 of 90 strings)

Translated using Weblate (Dutch)

Currently translated at 96.6% (87 of 90 strings)

Co-authored-by: Andreas Olsson <basen82@icloud.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Co-authored-by: SKNTim <timmy444074@gmail.com>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/nl/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/sv/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/zh_Hant/
Translation: Homebox/Frontend
2024-09-03 01:00:07 +00:00
Weblate
dbe77ea19d Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (90 of 90 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (90 of 90 strings)

Co-authored-by: Jackxwb <xwb9606@163.com>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/zh_Hans/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/zh_Hant/
Translation: Homebox/Frontend
2024-09-02 02:01:14 +00:00
Weblate
85e5c7e8e7 Translated using Weblate (Chinese (Simplified))
Currently translated at 97.7% (88 of 90 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.7% (88 of 90 strings)

Co-authored-by: Jackxwb <xwb9606@163.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/zh_Hans/
Translation: Homebox/Frontend
2024-09-01 16:38:10 +00:00
Weblate
3c273b370d Translated using Weblate (Chinese (Simplified))
Currently translated at 96.6% (87 of 90 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 96.6% (87 of 90 strings)

Co-authored-by: Jackxwb <xwb9606@163.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/zh_Hans/
Translation: Homebox/Frontend
2024-09-01 16:37:09 +00:00
Weblate
343e56b440 Translated using Weblate (Chinese (Simplified))
Currently translated at 95.5% (86 of 90 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 95.5% (86 of 90 strings)

Co-authored-by: Jackxwb <xwb9606@163.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/zh_Hans/
Translation: Homebox/Frontend
2024-09-01 16:36:35 +00:00
Weblate
3a949aee5a Translated using Weblate (Chinese (Simplified))
Currently translated at 70.0% (63 of 90 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 70.0% (63 of 90 strings)

Co-authored-by: Jackxwb <xwb9606@163.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/zh_Hans/
Translation: Homebox/Frontend
2024-09-01 16:26:19 +00:00
Weblate
1601e52c9c Translated using Weblate (Chinese (Simplified))
Currently translated at 65.5% (59 of 90 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 65.5% (59 of 90 strings)

Co-authored-by: Jackxwb <xwb9606@163.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/zh_Hans/
Translation: Homebox/Frontend
2024-09-01 16:21:10 +00:00
Weblate
760cc8e35c Translated using Weblate (Chinese (Simplified))
Currently translated at 46.6% (42 of 90 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 46.6% (42 of 90 strings)

Co-authored-by: Jackxwb <xwb9606@163.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/zh_Hans/
Translation: Homebox/Frontend
2024-09-01 16:13:52 +00:00
Weblate
6051e1fb8b Translated using Weblate (Chinese (Simplified))
Currently translated at 45.5% (41 of 90 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 45.5% (41 of 90 strings)

Co-authored-by: Jackxwb <xwb9606@163.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/zh_Hans/
Translation: Homebox/Frontend
2024-09-01 16:13:02 +00:00
Weblate
7b146947df Translated using Weblate (Chinese (Simplified))
Currently translated at 36.6% (33 of 90 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 36.6% (33 of 90 strings)

Co-authored-by: Jackxwb <xwb9606@163.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/zh_Hans/
Translation: Homebox/Frontend
2024-09-01 16:08:11 +00:00
Weblate
5497a10f9f Translated using Weblate (Chinese (Simplified))
Currently translated at 25.5% (23 of 90 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 25.5% (23 of 90 strings)

Co-authored-by: Jackxwb <xwb9606@163.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/zh_Hans/
Translation: Homebox/Frontend
2024-09-01 16:01:32 +00:00
Weblate
3e6f4b3657 Added translation using Weblate (Chinese (Simplified))
Co-authored-by: Jackxwb <xwb9606@163.com>
2024-09-01 15:45:59 +00:00
Weblate
7baf58ad61 Translated using Weblate (Turkish)
Currently translated at 100.0% (90 of 90 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (90 of 90 strings)

Co-authored-by: Hakan Bildir <divxtr@gmail.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/tr/
Translation: Homebox/Frontend
2024-09-01 10:42:51 +00:00
Matt Kilgore
d72437d18c Update config.mts 2024-08-31 22:38:55 -04:00
Weblate
ea57981953 Translated using Weblate (Turkish)
Currently translated at 100.0% (90 of 90 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (90 of 90 strings)

Co-authored-by: Hakan Bildir <divxtr@gmail.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/tr/
Translation: Homebox/Frontend
2024-08-31 19:57:38 +00:00
Weblate
c2d0cce02d Translated using Weblate (Turkish)
Currently translated at 71.1% (64 of 90 strings)

Translated using Weblate (Turkish)

Currently translated at 71.1% (64 of 90 strings)

Translated using Weblate (Turkish)

Currently translated at 71.1% (64 of 90 strings)

Co-authored-by: Hakan Bildir <divxtr@gmail.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Co-authored-by: Salad <selcuk.erbek@gmail.com>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/tr/
Translation: Homebox/Frontend
2024-08-31 19:51:29 +00:00
Weblate
9f7a119e95 Translated using Weblate (Russian)
Currently translated at 100.0% (90 of 90 strings)

Translated using Weblate (Turkish)

Currently translated at 58.8% (53 of 90 strings)

Co-authored-by: Fedor M <k930bx@gmail.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/ru/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/tr/
Translation: Homebox/Frontend
2024-08-31 19:48:29 +00:00
Weblate
0dacc97e99 Translated using Weblate (French)
Currently translated at 94.4% (85 of 90 strings)

Translated using Weblate (French)

Currently translated at 94.4% (85 of 90 strings)

Co-authored-by: Jean-Philippe Baril <weblate.org@alias.trebaxis.net>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/fr/
Translation: Homebox/Frontend
2024-08-30 19:48:35 +00:00
Weblate
52a44da56b Translated using Weblate (French)
Currently translated at 91.1% (82 of 90 strings)

Translated using Weblate (French)

Currently translated at 91.1% (82 of 90 strings)

Co-authored-by: Jean-Philippe Baril <weblate.org@alias.trebaxis.net>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/fr/
Translation: Homebox/Frontend
2024-08-29 19:28:00 +00:00
Weblate
7114f262c2 Translated using Weblate (French)
Currently translated at 90.0% (81 of 90 strings)

Translated using Weblate (French)

Currently translated at 90.0% (81 of 90 strings)

Co-authored-by: Jean-Philippe Baril <weblate.org@alias.trebaxis.net>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/fr/
Translation: Homebox/Frontend
2024-08-29 19:13:29 +00:00
Cosmo
7647ea96d1 added the ability to add a photo in the item creation modal (#173)
* added the ability to add a photo in the item creation modal

* fixed problem with create button being hidden on tiny screens

* fix: ui, translations

---------

Co-authored-by: Matt Kilgore <matthew@kilgore.dev>
2024-08-28 20:26:45 -04:00
Oliver Larsson
593da25cdb fix: CSV export not including item notes (#180) 2024-08-28 19:44:37 -04:00
Weblate
f22bce7ccb Translated using Weblate (Russian)
Currently translated at 100.0% (89 of 89 strings)

Translated using Weblate (German)

Currently translated at 100.0% (89 of 89 strings)

Co-authored-by: Dominik <account@kowanda.net>
Co-authored-by: Fedor M <k930bx@gmail.com>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/de/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/ru/
Translation: Homebox/Frontend
2024-08-28 05:09:49 +00:00
Romulo Gatto
1688773bba adding email validator (#178) 2024-08-25 18:46:55 -04:00
Weblate
b56b5d2400 Translated using Weblate (Russian)
Currently translated at 100.0% (89 of 89 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (89 of 89 strings)

Translated using Weblate (Turkish)

Currently translated at 57.3% (51 of 89 strings)

Co-authored-by: Fedor M <k930bx@gmail.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/ru/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/tr/
Translation: Homebox/Frontend
2024-08-25 18:48:35 +00:00
Weblate
33ee208071 Translated using Weblate (Russian)
Currently translated at 33.7% (30 of 89 strings)

Translated using Weblate (Russian)

Currently translated at 33.7% (30 of 89 strings)

Co-authored-by: Fedor M <k930bx@gmail.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/ru/
Translation: Homebox/Frontend
2024-08-24 18:39:26 +00:00
Weblate
fe880cc2c7 Translated using Weblate (Russian)
Currently translated at 25.8% (23 of 89 strings)

Translated using Weblate (Russian)

Currently translated at 25.8% (23 of 89 strings)

Co-authored-by: Fedor M <k930bx@gmail.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/ru/
Translation: Homebox/Frontend
2024-08-24 18:37:51 +00:00
Weblate
cffe57b74e Added translation using Weblate (Russian)
Co-authored-by: Fedor M <k930bx@gmail.com>
2024-08-24 18:25:04 +00:00
Weblate
66882d6fd9 Translated using Weblate (Spanish)
Currently translated at 100.0% (89 of 89 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (89 of 89 strings)

Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Co-authored-by: Slydite4 <39199098+Slydite4@users.noreply.github.com>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/es/
Translation: Homebox/Frontend
2024-08-22 18:36:20 +00:00
Weblate
050f22f051 Translated using Weblate (Spanish)
Currently translated at 60.6% (54 of 89 strings)

Translated using Weblate (Spanish)

Currently translated at 60.6% (54 of 89 strings)

Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Co-authored-by: Slydite4 <39199098+Slydite4@users.noreply.github.com>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/es/
Translation: Homebox/Frontend
2024-08-22 07:24:54 +00:00
Weblate
7891af3a9a Translated using Weblate (Spanish)
Currently translated at 58.4% (52 of 89 strings)

Translated using Weblate (Spanish)

Currently translated at 58.4% (52 of 89 strings)

Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Co-authored-by: Slydite4 <39199098+Slydite4@users.noreply.github.com>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/es/
Translation: Homebox/Frontend
2024-08-22 07:23:48 +00:00
Weblate
40cbccf50a Translated using Weblate (Spanish)
Currently translated at 56.1% (50 of 89 strings)

Translated using Weblate (Spanish)

Currently translated at 56.1% (50 of 89 strings)

Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Co-authored-by: Slydite4 <39199098+Slydite4@users.noreply.github.com>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/es/
Translation: Homebox/Frontend
2024-08-22 07:23:06 +00:00
Weblate
0348da362c Translated using Weblate (Spanish)
Currently translated at 51.6% (46 of 89 strings)

Translated using Weblate (Spanish)

Currently translated at 51.6% (46 of 89 strings)

Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Co-authored-by: Slydite4 <39199098+Slydite4@users.noreply.github.com>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/es/
Translation: Homebox/Frontend
2024-08-22 07:21:48 +00:00
Weblate
f0a3780f3a Added translation using Weblate (Spanish)
Co-authored-by: Slydite4 <39199098+Slydite4@users.noreply.github.com>
2024-08-22 07:00:49 +00:00
Weblate
39163f3cfc Translated using Weblate (French)
Currently translated at 88.7% (79 of 89 strings)

Translated using Weblate (French)

Currently translated at 88.7% (79 of 89 strings)

Co-authored-by: Julien <hydreliox@gmail.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/fr/
Translation: Homebox/Frontend
2024-08-16 06:40:28 +00:00
Weblate
43676ab407 Translated using Weblate (French)
Currently translated at 77.5% (69 of 89 strings)

Translated using Weblate (French)

Currently translated at 77.5% (69 of 89 strings)

Translated using Weblate (French)

Currently translated at 77.5% (69 of 89 strings)

Translated using Weblate (Dutch)

Currently translated at 96.6% (86 of 89 strings)

Co-authored-by: Julien <hydreliox@gmail.com>
Co-authored-by: Matthew Kilgore <matthew@kilgore.dev>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/fr/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/nl/
Translation: Homebox/Frontend
2024-08-15 18:28:54 +00:00
Lukas
639f795b9a [LANGUAGE UPDATE] Frontend translations for Italian and German (#170)
* Update it.json

Translate all strings into italian and reorder alphabetically

* Update de.json

Translated all strings into German and sorted them alphabetically.

* Update it.json

Added missing commas

* Update it.json

Fix indentation and remove unnecessary curly bracket left behind during the translation

* Replace 'Edit', 'Create', and 'Other' with their Italian equivalents

* Replace 'Edit', 'Create', and 'Other' with their German equivalents
2024-08-14 09:08:12 -04:00
Weblate
fc95d2cab8 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (89 of 89 strings)

Translated using Weblate (Dutch)

Currently translated at 94.3% (84 of 89 strings)

Translated using Weblate (Dutch)

Currently translated at 94.3% (84 of 89 strings)

Co-authored-by: 101br03k <warmerdamm03@gmail.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Co-authored-by: SKNTim <timmy444074@gmail.com>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/nl/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/zh_Hant/
Translation: Homebox/Frontend
2024-08-13 18:48:34 +00:00
Weblate
695b6d68e6 Added translation using Weblate (Chinese (Traditional))
Co-authored-by: SKNTim <timmy444074@gmail.com>
2024-08-13 02:40:59 +00:00
Weblate
b6c265098d Translated using Weblate (Swedish)
Currently translated at 100.0% (89 of 89 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (89 of 89 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (89 of 89 strings)

Translated using Weblate (Dutch)

Currently translated at 91.0% (81 of 89 strings)

Translated using Weblate (Dutch)

Currently translated at 91.0% (81 of 89 strings)

Co-authored-by: 101br03k <warmerdamm03@gmail.com>
Co-authored-by: Andreas Olsson <basen82@icloud.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Co-authored-by: Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/nl/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/sv/
Translation: Homebox/Frontend
2024-08-12 15:05:56 +00:00
Weblate
2a80d348bd Translated using Weblate (Swedish)
Currently translated at 80.8% (72 of 89 strings)

Translated using Weblate (Turkish)

Currently translated at 47.1% (42 of 89 strings)

Translated using Weblate (Dutch)

Currently translated at 74.1% (66 of 89 strings)

Translated using Weblate (Dutch)

Currently translated at 74.1% (66 of 89 strings)

Translated using Weblate (Dutch)

Currently translated at 74.1% (66 of 89 strings)

Co-authored-by: 101br03k <warmerdamm03@gmail.com>
Co-authored-by: Andreas Olsson <basen82@icloud.com>
Co-authored-by: Hakan Bildir <divxtr@gmail.com>
Co-authored-by: Matthew Kilgore <matthew@kilgore.dev>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/nl/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/sv/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/tr/
Translation: Homebox/Frontend
2024-08-12 10:16:48 +00:00
Weblate
c6542de93d Translated using Weblate (Swedish)
Currently translated at 58.4% (52 of 89 strings)

Translated using Weblate (Turkish)

Currently translated at 39.3% (35 of 89 strings)

Translated using Weblate (Turkish)

Currently translated at 39.3% (35 of 89 strings)

Translated using Weblate (French)

Currently translated at 65.1% (58 of 89 strings)

Translated using Weblate (German)

Currently translated at 58.4% (52 of 89 strings)

Translated using Weblate (Italian)

Currently translated at 39.3% (35 of 89 strings)

Translated using Weblate (Italian)

Currently translated at 39.3% (35 of 89 strings)

Translated using Weblate (Dutch)

Currently translated at 61.7% (55 of 89 strings)

Translated using Weblate (Dutch)

Currently translated at 61.7% (55 of 89 strings)

Co-authored-by: 101br03k <warmerdamm03@gmail.com>
Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Matthew Kilgore <matthew@kilgore.dev>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/de/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/fr/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/it/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/nl/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/sv/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/tr/
Translation: Homebox/Frontend
2024-08-11 23:45:32 +00:00
Weblate
5928678564 Update translation files
Updated by "Cleanup translation files" add-on in Weblate.

Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/
Translation: Homebox/Frontend
2024-08-11 23:44:45 +00:00
Matt Kilgore
fac52ca122 chore: alphabetize english JSON 2024-08-11 19:42:03 -04:00
Matt Kilgore
e9d270269f Merge remote-tracking branch 'origin/main' 2024-08-11 19:40:27 -04:00
Matt Kilgore
0a4c5fbb28 chore: translate items page 2024-08-11 19:40:13 -04:00
Weblate
2bfb0283d9 Translated using Weblate (Dutch)
Currently translated at 90.1% (55 of 61 strings)

Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/nl/
Translation: Homebox/Frontend
2024-08-11 19:46:14 +00:00
Weblate
94e8aee36f Translated using Weblate (French)
Currently translated at 96.7% (59 of 61 strings)

Translated using Weblate (Dutch)

Currently translated at 85.2% (52 of 61 strings)

Co-authored-by: 101br03k <warmerdamm03@gmail.com>
Co-authored-by: Chevdor <chevdor@gmail.com>
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/fr/
Translate-URL: https://translate.sysadminsmedia.com/projects/homebox/frontend/nl/
Translation: Homebox/Frontend
2024-08-11 19:45:11 +00:00
Matthew Kilgore
8051956a2e Deleted translation using Weblate (English (Pirate)) 2024-08-11 18:51:00 +00:00
Matt Kilgore
c7020503be Merge remote-tracking branch 'origin/main' 2024-08-11 14:09:58 -04:00
Matt Kilgore
28edce96d9 chore: Finish profile page translations 2024-08-11 14:09:51 -04:00
Weblate
b4481fcc84 Translated using Weblate (German)
Currently translated at 100.0% (53 of 53 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (53 of 53 strings)

Co-authored-by: Andreas Olsson <basen82@icloud.com>
Co-authored-by: Philipp Walter <philippwalter21@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: http://translate.sysadminsmedia.com/projects/homebox/frontend/de/
Translate-URL: http://translate.sysadminsmedia.com/projects/homebox/frontend/sv/
Translation: Homebox/Frontend
2024-08-11 07:11:39 +00:00
Weblate
2be2bebb4e Translated using Weblate (Swedish)
Currently translated at 100.0% (53 of 53 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (53 of 53 strings)

Co-authored-by: Andreas Olsson <basen82@icloud.com>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: http://translate.sysadminsmedia.com/projects/homebox/frontend/sv/
Translation: Homebox/Frontend
2024-08-10 05:47:28 +00:00
Weblate
d4bb8def62 Added translation using Weblate (Swedish)
Co-authored-by: Andreas Olsson <basen82@icloud.com>
2024-08-10 05:22:48 +00:00
Weblate
7442cb01b7 Translated using Weblate (French)
Currently translated at 96.2% (51 of 53 strings)

Translated using Weblate (English)

Currently translated at 100.0% (53 of 53 strings)

Co-authored-by: Matthew Kilgore <matthew@kilgore.dev>
Co-authored-by: MyMemory <noreply-mt-mymemory@weblate.org>
Translate-URL: http://translate.sysadminsmedia.com/projects/homebox/frontend/en/
Translate-URL: http://translate.sysadminsmedia.com/projects/homebox/frontend/fr/
Translation: Homebox/Frontend
2024-08-09 14:48:19 +00:00
Weblate
95ba8275e8 Translated using Weblate (Dutch)
Currently translated at 92.4% (49 of 53 strings)

Translated using Weblate (French)

Currently translated at 98.1% (52 of 53 strings)

Translated using Weblate (English (Pirate))

Currently translated at 13.3% (6 of 45 strings)

Co-authored-by: 101br03k <warmerdamm03@gmail.com>
Co-authored-by: Matthew Kilgore <matthew@kilgore.dev>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: thhurt <th.hurtado+weblate@gmail.com>
Translate-URL: http://translate.sysadminsmedia.com/projects/homebox/frontend/en@pirate/
Translate-URL: http://translate.sysadminsmedia.com/projects/homebox/frontend/fr/
Translate-URL: http://translate.sysadminsmedia.com/projects/homebox/frontend/nl/
Translation: Homebox/Frontend
2024-08-08 14:45:24 +00:00
Matt Kilgore
2f4a0dd212 fix: #146 add version info to every page 2024-08-07 21:55:05 -04:00
Matt Kilgore
52a621e9ba fix: use the browser default language 2024-08-07 20:57:40 -04:00
Matt Kilgore
1f77fad829 fix: i18n ICU loading failure due to original auto-loading. 2024-08-07 20:26:40 -04:00
Weblate
8d93a3f56e Translated using Weblate (German)
Currently translated at 100.0% (35 of 35 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (35 of 35 strings)

Co-authored-by: Matthew Kilgore <matthew@kilgore.dev>
Translate-URL: http://translate.sysadminsmedia.com/projects/homebox/frontend/de/
Translate-URL: http://translate.sysadminsmedia.com/projects/homebox/frontend/it/
Translation: Homebox/Frontend
2024-08-06 13:54:10 +00:00
Weblate
9c572e7ab2 Translated using Weblate (French)
Currently translated at 100.0% (35 of 35 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (35 of 35 strings)

Translated using Weblate (German)

Currently translated at 100.0% (35 of 35 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (35 of 35 strings)

Co-authored-by: Hakan Bildir <divxtr@gmail.com>
Co-authored-by: Jean-Philippe Baril <weblate.org@alias.trebaxis.net>
Co-authored-by: Lukas Hofer <naluso@protonmail.com>
Translate-URL: http://translate.sysadminsmedia.com/projects/homebox/frontend/de/
Translate-URL: http://translate.sysadminsmedia.com/projects/homebox/frontend/fr/
Translate-URL: http://translate.sysadminsmedia.com/projects/homebox/frontend/it/
Translate-URL: http://translate.sysadminsmedia.com/projects/homebox/frontend/tr/
Translation: Homebox/Frontend
2024-08-05 13:04:00 +00:00
A3
1f15e74730 changed companyname and url to sysadminsmedia.com (#158) 2024-08-05 08:59:53 -04:00
Weblate
7570a04c02 Added translation using Weblate (Turkish)
Co-authored-by: Hakan Bildir <divxtr@gmail.com>
2024-08-05 07:56:36 +00:00
Weblate
fc2e89c448 Added translation using Weblate (French)
Co-authored-by: Jean-Philippe Baril <weblate.org@alias.trebaxis.net>
2024-08-05 07:56:25 +00:00
Weblate
be216ff7fe Translated using Weblate (German)
Currently translated at 40.0% (14 of 35 strings)

Co-authored-by: Benjamin Kahlau <nyhkkbjyek@roanapur.de>
Translate-URL: http://translate.sysadminsmedia.com/projects/homebox/frontend/de/
Translation: Homebox/Frontend
2024-08-05 00:10:39 +00:00
Weblate
388208571b Added translation using Weblate (German)
Co-authored-by: Benjamin Kahlau <nyhkkbjyek@roanapur.de>
2024-08-04 22:34:52 +00:00
Weblate
1891903007 Translated using Weblate (English (Pirate))
Currently translated at 11.4% (4 of 35 strings)

Co-authored-by: Matthew Kilgore <matthew@kilgore.dev>
Translate-URL: http://translate.sysadminsmedia.com/projects/homebox/frontend/en@pirate/
Translation: Homebox/Frontend
2024-08-04 22:25:05 +00:00
Matt Kilgore
41a7e73ff4 Merge remote-tracking branch 'origin/main' 2024-08-04 17:36:11 -04:00
Matt Kilgore
81d9fb0700 chore: add translations to contributions sections 2024-08-04 17:36:02 -04:00
Weblate
76312d6eb6 Added translation using Weblate (Italian)
Co-authored-by: Lukas Hofer <naluso@protonmail.com>
2024-08-04 21:26:53 +00:00
Weblate
f31528c841 Added translation using Weblate (English (Pirate))
Co-authored-by: Matthew Kilgore <matthew@kilgore.dev>
2024-08-04 20:53:17 +00:00
Weblate
6d869fdece Translated using Weblate (Dutch)
Currently translated at 100.0% (35 of 35 strings)

Translated using Weblate (English)

Currently translated at 100.0% (35 of 35 strings)

Co-authored-by: 101br03k <warmerdamm03@gmail.com>
Co-authored-by: Weblate Admin <admin@sysadminsmedia.com>
Translate-URL: http://translate.sysadminsmedia.com/projects/homebox/frontend/en/
Translate-URL: http://translate.sysadminsmedia.com/projects/homebox/frontend/nl/
Translation: Homebox/Frontend
2024-08-04 20:16:58 +00:00
Weblate
d0784a7773 Added translation using Weblate (Dutch)
Co-authored-by: Matthew Kilgore <matthew@kilgore.dev>
2024-08-04 19:27:09 +00:00
Weblate Admin
791f843bc8 Deleted translation using Weblate (Chinese (Simplified)) 2024-08-04 18:21:20 +00:00
Matt Kilgore
a0cdb231fd chore: translate components 2024-08-04 14:08:05 -04:00
Matt Kilgore
e0004842e6 fix: bad translation 2024-08-04 13:37:37 -04:00
Matt Kilgore
fdbfa0e76f refactor: better translation formatting 2024-08-04 13:11:27 -04:00
Matt Kilgore
005516013f chore: delete unneeded translation file 2024-08-04 12:56:12 -04:00
Weblate
9ec3dd4b16 Added translation using Weblate (Chinese (Simplified))
Translated using Weblate (English)

Currently translated at 100.0% (19 of 19 strings)

Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Weblate Admin <admin@sysadminsmedia.com>
Translate-URL: http://translate.sysadminsmedia.com/projects/homebox/frontend/en/
Translation: Homebox/Frontend
2024-08-04 16:47:43 +00:00
Matt Kilgore
4dacf981a9 Merge branch 'refs/heads/mk/i18n'
# Conflicts:
#	frontend/pages/index.vue
2024-08-04 11:35:37 -04:00
Matt Kilgore
9f7b76b37d chore: minor tweak 2024-08-04 11:31:16 -04:00
Matt Kilgore
0d51558e74 chore: more translations 2024-08-04 11:26:07 -04:00
Matt Kilgore
236c257892 feat: adding initial i18n support (#155) 2024-08-04 11:02:29 -04:00
Matt Kilgore
3540ce4297 feat: adding initial i18n support 2024-08-03 23:04:26 -04:00
Rylie Pavlik
0bcb155756 Fixes to the Tools page (#154)
* Correct the description of the import feature on the tools page.

* Fix page title on Tools page.
2024-08-01 21:52:13 -04:00
Fuzzy
12219522ab Document search tips, cleanup documentation (#152)
* Update tips-tricks.md

Add instructions for search by ID

* Update get-started.md

Fixing typos and redundancies
2024-07-31 19:51:43 -04:00
Katos
13864997ab Merge pull request #149 from victorhooi/patch-1
Fix small typo in label discussion thread URL
2024-07-27 16:01:35 +01:00
Victor Hooi
2ab2766534 Fix small typo in label discussion thread URL 2024-07-28 00:56:49 +10:00
Matt Kilgore
e051352070 chore: update openapi documentation (#148) 2024-07-27 09:10:47 -04:00
Matt Kilgore
3bf1e50620 Splitting dependencies into separate docker layers (better caching) (#142)
* Initial test with NodeJS

* Fix screw up

* Try again

* Try Golang caching

* Test with some more cache
2024-07-21 10:40:03 -04:00
github-actions[bot]
42f3c88396 Update currencies.json (#143)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Matt Kilgore <tankerkiller125@users.noreply.github.com>
2024-07-21 10:39:47 -04:00
Ryan Sheppard
c9f31ef934 Enable Sorting on Search By Created and Updated At (#140)
- Required updating query value to use first.value
  to set the value of the initial sort

- Ordering starts with name but can be changed to
  createdAt or updatedAt by the user
2024-07-21 09:30:03 -04:00
Ryan Sheppard
01f54aeb52 Add URL column to CSV export for items (#141)
* feat: Add URL column to CSV export for items

Enhanced the CSV export functionality to include a URL field for each item. This change required updating the export logic to generate and include item URLs based on the request's referer header.

* chore: add URL configuration to V1Controller for dynamic URL handling in item export
2024-07-21 09:29:46 -04:00
PatrickDaG
2d1016d362 Add nmprc to automatically hoist dependencies (#136) 2024-07-17 07:47:35 -04:00
Katos
b48c961ac1 Merge pull request #133 from sysadminsmedia/update-currencies
Update currencies
2024-07-14 17:25:08 +01:00
github-actions[bot]
4d47567995 Update currencies.json 2024-07-14 16:21:40 +00:00
Katos
6b2e3accf7 Merge pull request #132 from sysadminsmedia/katos/currencies-workflow
Force rebase on Update-currencies action
2024-07-14 17:21:27 +01:00
Katos
c1f8520c4f Rectify issue with Github action 2024-07-14 17:20:50 +01:00
Katos
41eb99ec40 Force rebase on Update-currencies action 2024-07-14 16:45:04 +01:00
Katos
97a74127fb Merge pull request #131 from sysadminsmedia/katos/currencies-api-sync
Skip commit attempt if currencies already synced with API
2024-07-14 16:38:31 +01:00
Katos
a9396167bf Fix my stupid mistakes.... 2024-07-14 16:35:28 +01:00
Katos
3385e5684e Update currencies automation to use a PR instead of comitting directly 2024-07-14 16:33:42 +01:00
Katos
bb9672214c Update error handling for Currencies sync, if already synced then just skip. 2024-07-14 16:25:41 +01:00
Katos
1b93672417 Merge pull request #130 from sysadminsmedia/katosdev-patch-1
Update update_currencies.py to fix error handling
2024-07-14 16:23:47 +01:00
Katos
8b1cedd4a8 Update update_currencies.py to fix error handling 2024-07-14 16:08:37 +01:00
Katos
2c34047b6d Merge pull request #129 from sysadminsmedia/katos/currencies-auto-update
Create a Github Workflow to keep currencies in sync from API
2024-07-14 16:05:48 +01:00
Katos
f0942f0714 Add error handling to API pulls 2024-07-14 16:00:25 +01:00
Katos
967e574ea8 Create a Github Workflow to keep currencies in sync from API 2024-07-14 15:52:25 +01:00
58 changed files with 11362 additions and 6156 deletions

65
.github/scripts/update_currencies.py vendored Normal file
View File

@@ -0,0 +1,65 @@
import requests
import json
import os
def fetch_currencies():
try:
response = requests.get('https://restcountries.com/v3.1/all')
response.raise_for_status()
except requests.exceptions.Timeout:
print("Request to the API timed out.")
return []
except requests.exceptions.RequestException as e:
print(f"An error occurred while making the request: {e}")
return []
try:
countries = response.json()
except json.JSONDecodeError:
print("Failed to decode JSON from the response.")
return []
currencies_list = []
for country in countries:
country_name = country.get('name', {}).get('common')
country_currencies = country.get('currencies', {})
for currency_code, currency_info in country_currencies.items():
symbol = currency_info.get('symbol', '')
currencies_list.append({
'code': currency_code,
'local': country_name,
'symbol': symbol,
'name': currency_info.get('name')
})
return currencies_list
def save_currencies(currencies, file_path):
try:
os.makedirs(os.path.dirname(file_path), exist_ok=True)
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(currencies, f, ensure_ascii=False, indent=4)
except IOError as e:
print(f"An error occurred while writing to the file: {e}")
def load_existing_currencies(file_path):
try:
with open(file_path, 'r', encoding='utf-8') as f:
return json.load(f)
except (IOError, json.JSONDecodeError):
return [] # Return an empty list if file doesn't exist or is invalid
def main():
save_path = 'backend/internal/core/currencies/currencies.json'
existing_currencies = load_existing_currencies(save_path)
new_currencies = fetch_currencies()
if new_currencies == existing_currencies:
print("Currencies up-to-date with API, skipping commit.")
else:
save_currencies(new_currencies, save_path)
print("Currencies updated and saved.")
if __name__ == "__main__":
main()

View File

@@ -92,8 +92,8 @@ jobs:
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
platforms: linux/amd64,linux/arm64,linux/arm/v7
# cache-from: type=gha
# cache-to: type=gha,mode=max
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ github.ref_name }}
COMMIT=${{ github.sha }}

View File

@@ -89,8 +89,8 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64,linux/arm/v7
# cache-from: type=gha
# cache-to: type=gha,mode=max
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ github.ref_name }}
COMMIT=${{ github.sha }}

100
.github/workflows/update-currencies.yml vendored Normal file
View File

@@ -0,0 +1,100 @@
name: Update Currencies
on:
push:
branches:
- main
jobs:
update-currencies:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.8'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests
- name: Run currency fetch script
run: python .github/scripts/update_currencies.py
- name: Check for changes
id: check_changes
run: |
if [[ $(git status --porcelain) ]]; then
echo "Changes detected."
echo "changes=true" >> $GITHUB_ENV
else
echo "No changes detected."
echo "changes=false" >> $GITHUB_ENV
fi
- name: Delete existing update-currencies branch
run: |
if git show-ref --verify --quiet refs/heads/update-currencies; then
git branch -D update-currencies
echo "Deleted existing update-currencies branch."
else
echo "No existing update-currencies branch to delete."
fi
- name: Create new update-currencies branch
if: env.changes == 'true'
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
# Create a new branch
git checkout -b update-currencies
git add backend/internal/core/currencies/currencies.json
git commit -m "Update currencies.json"
# Fetch the latest changes from the remote
git fetch origin
# Attempt to rebase with the latest changes
if git show-ref --verify --quiet refs/remotes/origin/update-currencies; then
if ! git rebase origin/update-currencies; then
echo "Rebase conflicts occurred. Please resolve them manually."
echo "To resolve conflicts, check out the 'update-currencies' branch locally."
exit 1
fi
else
echo "No existing remote branch 'update-currencies'. Skipping rebase."
fi
# Push the new branch to the remote
if ! git push --set-upstream origin update-currencies; then
echo "Push failed, trying to fetch and rebase again."
git fetch origin
if git show-ref --verify --quiet refs/remotes/origin/update-currencies; then
if ! git rebase origin/update-currencies; then
echo "Second rebase failed. Please resolve manually."
exit 1
fi
else
echo "No existing remote branch 'update-currencies'. Skipping rebase."
fi
if ! git push --set-upstream origin update-currencies; then
echo "Second push failed. Please resolve manually."
exit 1
fi
fi
# Create a pull request
curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-X POST \
-d '{"title": "Update currencies", "head": "update-currencies", "base": "main"}' \
https://api.github.com/repos/${{ github.repository }}/pulls
- name: Notify no changes
if: env.changes == 'false'
run: echo "Currencies up-to-date with API, skipping commit."

View File

@@ -1,13 +1,23 @@
# Node dependencies
FROM node:18-alpine AS frontend-dependencies
WORKDIR /app
RUN npm install -g pnpm
COPY frontend/package.json frontend/pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile --shamefully-hoist
# Build Nuxt
FROM node:18-alpine AS frontend-builder
WORKDIR /app
RUN npm install -g pnpm
COPY frontend/package.json frontend/pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile --shamefully-hoist
COPY frontend .
COPY --from=frontend-dependencies /app/node_modules ./node_modules
RUN pnpm build
FROM golang:alpine AS builder-dependencies
WORKDIR /go/src/app
COPY ./backend .
RUN go mod download
# Build API
FROM golang:alpine AS builder
ARG BUILD_TIME
@@ -19,10 +29,11 @@ RUN apk update && \
WORKDIR /go/src/app
COPY ./backend .
RUN go get -d -v ./...
RUN rm -rf ./app/api/public
COPY --from=frontend-builder /app/.output/public ./app/api/static/public
RUN CGO_ENABLED=0 GOOS=linux go build \
COPY --from=builder-dependencies /go/pkg/mod /go/pkg/mod
RUN --mount=type=cache,target=/root/.cache/go-build \
CGO_ENABLED=0 GOOS=linux go build \
-ldflags "-s -w -X main.commit=$COMMIT -X main.buildTime=$BUILD_TIME -X main.version=$VERSION" \
-o /go/bin/api \
-v ./app/api/*.go

View File

@@ -1,35 +1,42 @@
# Node dependencies
FROM node:18-alpine AS frontend-dependencies
WORKDIR /app
RUN npm install -g pnpm
COPY frontend/package.json frontend/pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile --shamefully-hoist
# Build Nuxt
FROM node:18-alpine AS frontend-builder
WORKDIR /app
RUN npm install -g pnpm
COPY frontend/package.json frontend/pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile --shamefully-hoist
COPY frontend .
COPY --from=frontend-dependencies /app/node_modules ./node_modules
RUN pnpm build
FROM golang:alpine AS builder-dependencies
WORKDIR /go/src/app
COPY ./backend .
RUN go mod download
# Build API
FROM golang:alpine AS builder
ARG BUILD_TIME
ARG COMMIT
ARG VERSION
ARG BUSYBOX_VERSION=1.36.1-r31
RUN apk update && \
apk upgrade && \
apk add --update git build-base gcc g++
WORKDIR /go/src/app
COPY ./backend .
RUN go get -d -v ./...
RUN rm -rf ./app/api/public
COPY --from=frontend-builder /app/.output/public ./app/api/static/public
RUN CGO_ENABLED=0 GOOS=linux go build \
COPY --from=builder-dependencies /go/pkg/mod /go/pkg/mod
RUN --mount=type=cache,target=/root/.cache/go-build \
CGO_ENABLED=0 GOOS=linux go build \
-ldflags "-s -w -X main.commit=$COMMIT -X main.buildTime=$BUILD_TIME -X main.version=$VERSION" \
-o /go/bin/api \
-v ./app/api/*.go && \
chmod +x /go/bin/api && \
# create a directory so that we can copy it in the next stage
mkdir /data
-v ./app/api/*.go
FROM gcr.io/distroless/java:latest

View File

@@ -49,6 +49,11 @@ Contributions are what make the open source community such an amazing place to l
If you are not a coder, you can still contribute financially. Financial contributions help me prioritize working on this project over others and helps me know that there is a real demand for project development.
## Help us Translate
We want to make sure that Homebox is available in as many languages as possible. If you are interested in helping us translate Homebox, please help us via our [Weblate instance](https://translate.sysadminsmedia.com/projects/homebox/).
[![Translation status](http://translate.sysadminsmedia.com/widget/homebox/multi-auto.svg)](http://translate.sysadminsmedia.com/engage/homebox/)
## Credits
- Original project by [@hay-kot](https://github.com/hay-kot)

View File

@@ -57,6 +57,12 @@ func WithSecureCookies(secure bool) func(*V1Controller) {
}
}
func WithURL(url string) func(*V1Controller) {
return func(ctrl *V1Controller) {
ctrl.url = url
}
}
type V1Controller struct {
cookieSecure bool
repo *repo.AllRepos
@@ -65,6 +71,7 @@ type V1Controller struct {
isDemo bool
allowRegistration bool
bus *eventbus.EventBus
url string
}
type (

View File

@@ -6,6 +6,7 @@ import (
"errors"
"math/big"
"net/http"
"net/url"
"strings"
"github.com/google/uuid"
@@ -333,7 +334,7 @@ func (ctrl *V1Controller) HandleItemsExport() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
ctx := services.NewContext(r.Context())
csvData, err := ctrl.svc.Items.ExportCSV(r.Context(), ctx.GID)
csvData, err := ctrl.svc.Items.ExportCSV(r.Context(), ctx.GID, getHBURL(r.Header.Get("Referer"), ctrl.url))
if err != nil {
log.Err(err).Msg("failed to export items")
return validate.NewRequestError(err, http.StatusInternalServerError)
@@ -347,3 +348,26 @@ func (ctrl *V1Controller) HandleItemsExport() errchain.HandlerFunc {
return writer.WriteAll(csvData)
}
}
func getHBURL(refererHeader, fallback string) (hbURL string) {
hbURL = refererHeader
if hbURL == "" {
hbURL = fallback
}
return stripPathFromURL(hbURL)
}
// stripPathFromURL removes the path from a URL.
// ex. https://example.com/tools -> https://example.com
func stripPathFromURL(rawURL string) string {
parsedURL, err := url.Parse(rawURL)
if err != nil {
log.Err(err).Msg("failed to parse URL")
return ""
}
strippedURL := url.URL{Scheme: parsedURL.Scheme, Host: parsedURL.Host}
return strippedURL.String()
}

View File

@@ -3,12 +3,7 @@ package main
import (
"embed"
"errors"
"io"
"mime"
"net/http"
"path"
"path/filepath"
"fmt"
"github.com/go-chi/chi/v5"
"github.com/hay-kot/httpkit/errchain"
httpSwagger "github.com/swaggo/http-swagger/v2" // http-swagger middleware
@@ -18,6 +13,11 @@ import (
_ "github.com/sysadminsmedia/homebox/backend/app/api/static/docs"
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authroles"
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
"io"
"mime"
"net/http"
"path"
"path/filepath"
)
const prefix = "/api"
@@ -54,6 +54,7 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
v1.WithMaxUploadSize(a.conf.Web.MaxUploadSize),
v1.WithRegistration(a.conf.Options.AllowRegistration),
v1.WithDemoStatus(a.conf.Demo), // Disable Password Change in Demo Mode
v1.WithURL(fmt.Sprintf("%s:%s", a.conf.Web.Host, a.conf.Web.Port)),
)
r.Route(prefix+"/v1", func(r chi.Router) {

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,7 @@ type ExportCSVRow struct {
LabelStr LabelString `csv:"HB.labels"`
AssetID repo.AssetID `csv:"HB.asset_id"`
Archived bool `csv:"HB.archived"`
URL string `csv:"HB.url"`
Name string `csv:"HB.name"`
Quantity int `csv:"HB.quantity"`

View File

@@ -153,7 +153,7 @@ func (s *IOSheet) Read(data io.Reader) error {
}
// ReadItems writes the sheet to a writer.
func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.UUID, repos *repo.AllRepos) error {
func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.UUID, repos *repo.AllRepos, hbURL string) error {
s.Rows = make([]ExportCSVRow, len(items))
extraHeaders := map[string]struct{}{}
@@ -178,6 +178,8 @@ func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.
labelString[i] = l.Name
}
url := generateItemURL(item, hbURL)
customFields := make([]ExportItemFields, len(item.Fields))
for i, f := range item.Fields {
@@ -201,6 +203,7 @@ func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.
Description: item.Description,
Insured: item.Insured,
Archived: item.Archived,
URL: url,
PurchasePrice: item.PurchasePrice,
PurchaseFrom: item.PurchaseFrom,
@@ -219,6 +222,7 @@ func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.
SoldPrice: item.SoldPrice,
SoldNotes: item.SoldNotes,
Notes: item.Notes,
Fields: customFields,
}
}
@@ -252,6 +256,14 @@ func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.
return nil
}
func generateItemURL(item repo.ItemOut, d string) string {
url := ""
if item.ID != uuid.Nil {
url = fmt.Sprintf("%s/item/%s", d, item.ID.String())
}
return url
}
// CSV writes the current sheet to a 2d array, for compatibility with TSV/CSV files.
func (s *IOSheet) CSV() ([][]string, error) {
memcsv := make([][]string, len(s.Rows)+1)

View File

@@ -329,7 +329,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data io.Re
return finished, nil
}
func (svc *ItemService) ExportCSV(ctx context.Context, GID uuid.UUID) ([][]string, error) {
func (svc *ItemService) ExportCSV(ctx context.Context, GID uuid.UUID, hbURL string) ([][]string, error) {
items, err := svc.repo.Items.GetAll(ctx, GID)
if err != nil {
return nil, err
@@ -337,7 +337,7 @@ func (svc *ItemService) ExportCSV(ctx context.Context, GID uuid.UUID) ([][]strin
sheet := reporting.IOSheet{}
err = sheet.ReadItems(ctx, items, GID, svc.repo)
err = sheet.ReadItems(ctx, items, GID, svc.repo, hbURL)
if err != nil {
return nil, err
}

View File

@@ -29,9 +29,9 @@ func (tp *TemplateProps) Set(key, value string) {
func DefaultTemplateData() TemplateProps {
return TemplateProps{
Defaults: TemplateDefaults{
CompanyName: "Haybytes.com",
CompanyName: "sysadminsmedia.com",
CompanyAddress: "123 Main St, Anytown, CA 12345",
CompanyURL: "https://haybytes.com",
CompanyURL: "https://sysadminsmedia.com",
ActivateAccountURL: "https://google.com",
UnsubscribeURL: "https://google.com",
},

View File

@@ -6,7 +6,7 @@ export default defineConfig({
description: "A simple home inventory management software",
lastUpdated: true,
sitemap: {
hostname: 'https://homebox.sysadminsmedia.com',
hostname: 'https://homebox.software',
},
locales: {

View File

@@ -1683,12 +1683,14 @@
"parameters": [
{
"type": "string",
"example": "admin@admin.com",
"description": "string",
"name": "username",
"in": "formData"
},
{
"type": "string",
"example": "admin",
"description": "string",
"name": "password",
"in": "formData"

View File

@@ -47,24 +47,28 @@ type checking `task ui:check`
## Documentation
We use [Vitepress](https://vitepress.dev/) for the web documentation of homebox. Anyone is welcome to contribute the documentation if they wish.
Anyone is welcome to contribute the documentation if they wish. For documentation contributions, you only need Node.js and PNPM.
For documentation contributions, you only need Node.js and PNPM.
::: info Notes
- Languages are separated by folder (e.g `/en`, `/fr`, etc.)
- The Sidebar must be updated on a per language basis
+ The Sidebar must be updated on a per-language basis
- Each languages files can be named independently (slugs can match the language)
- Each language's files can be named independently (slugs can match the language)
- The `public/_redirects` file is used to redirect the default to english
- Redirects can also be configured per language by adding `Language=` after the redirect code
:::
## Translations
We use our own [Weblate instance](https://translate.sysadminsmedia.com/projects/homebox/) for translations. If you would like to help translate Homebox, please visit the Weblate instance and help us translate the project.
[![Translation status](http://translate.sysadminsmedia.com/widget/homebox/multi-auto.svg)](http://translate.sysadminsmedia.com/engage/homebox/)
## Branch Flow
We use the `main` branch as the development branch. All PRs should be made to the `main` branch form a feature branch.
We use the `main` branch as the development branch. All PRs should be made to the `main` branch from a feature branch.
To create a pull request you can use the following steps:
1. Fork the repo and create a new branch from `main`
2. If you added code that should be tested, add tests
3. If you've changed APIs update the documentation
3. If you've changed APIs, update the documentation
4. Ensure that the test suite and linters pass
5. Create your PR

View File

@@ -21,6 +21,8 @@ Homebox provides the option to auto-set asset IDs, this is the default behavior.
Example ID: `000-001`
To search for an Asset ID: type `#` in the search bar followed by the ID you're searching for, e.g. `#000-001`.
Asset IDs are partially managed by Homebox, but have a flexible implementation to allow for unique use cases. IDs are non-unique at the database level, so there is nothing stopping a user from manually setting duplicate IDs for various items. There are two recommended approaches to manage Asset IDs:
### 1. Auto Incrementing IDs

1
frontend/.npmrc Normal file
View File

@@ -0,0 +1 @@
shamefully-hoist=true

View File

@@ -1,9 +1,8 @@
<template>
<BaseModal v-model="dialog">
<template #title> Import CSV File </template>
<template #title> {{ $t("components.app.import_dialog.title") }} </template>
<p>
Import a CSV file containing your items, labels, and locations. See documentation for more information on the
required format.
{{ $t("components.app.import_dialog.description") }}
</p>
<div class="alert alert-warning shadow-lg mt-4">
<div>
@@ -21,8 +20,7 @@
/>
</svg>
<span class="text-sm">
Behavior for imports with existing import_refs has changed. If an import_ref is present in the CSV file, the
item will be updated with the values in the CSV file.
{{ $t("components.app.import_dialog.change_warning") }}
</span>
</div>
</div>
@@ -33,7 +31,7 @@
<BaseButton type="button" @click="uploadCsv">
<MdiUpload class="h-5 w-5 mr-2" />
Upload
{{ $t("components.app.import_dialog.upload") }}
</BaseButton>
<p class="text-center pt-4 -mb-5">
{{ importCsv?.name }}
@@ -41,7 +39,7 @@
</div>
<div class="modal-action">
<BaseButton type="submit" :disabled="!importCsv"> Submit </BaseButton>
<BaseButton type="submit" :disabled="!importCsv"> {{ $t("global.submit") }} </BaseButton>
</div>
</form>
</BaseModal>

View File

@@ -9,23 +9,28 @@
{{ name != "" ? itm[name] : itm }}
</span>
</div>
<ul
<div
tabindex="0"
style="display: inline"
class="dropdown-content mb-1 menu shadow border border-gray-400 rounded bg-base-100 w-full z-[9999] max-h-60 overflow-y-scroll"
class="dropdown-content mb-1 menu w-full z-[9999] shadow border border-gray-400 rounded bg-base-100"
>
<li
v-for="(obj, idx) in items"
:key="idx"
:class="{
bordered: selected[idx],
}"
>
<button type="button" @click="toggle(idx)">
{{ name != "" ? obj[name] : obj }}
</button>
</li>
</ul>
<div class="m-2">
<input v-model="search" placeholder="Search…" class="input input-sm input-bordered w-full" />
</div>
<ul class="overflow-y-scroll max-h-60">
<li
v-for="(obj, idx) in filteredItems"
:key="idx"
:class="{
bordered: selected.includes(obj[props.uniqueField]),
}"
>
<button type="button" @click="toggle(obj[props.uniqueField])">
{{ name != "" ? obj[name] : obj }}
</button>
</li>
</ul>
</div>
</div>
</div>
</template>
@@ -49,6 +54,10 @@
type: String,
default: "name",
},
uniqueField: {
type: String,
default: "id",
},
selectFirst: {
type: Boolean,
default: false,
@@ -57,19 +66,26 @@
const value = useVModel(props, "modelValue", emit);
const selected = computed<Record<number, boolean>>(() => {
const obj: Record<number, boolean> = {};
value.value.forEach(itm => {
const idx = props.items.findIndex(item => item[props.name] === itm.name);
obj[idx] = true;
const search = ref("");
const filteredItems = computed(() => {
if (!search.value) {
return props.items;
}
return props.items.filter(item => {
return item[props.name].toLowerCase().includes(search.value.toLowerCase());
});
return obj;
});
function toggle(index: number) {
const item = props.items[index];
if (selected.value[index]) {
value.value = value.value.filter(itm => itm.name !== item.name);
const selected = computed<string[]>(() => {
return value.value.map(itm => itm[props.uniqueField]);
});
function toggle(uniqueField: string) {
const item = props.items.find(itm => itm[props.uniqueField] === uniqueField);
if (selected.value.includes(item[props.uniqueField])) {
value.value = value.value.filter(itm => itm[props.uniqueField] !== item[props.uniqueField]);
} else {
value.value = [...value.value, item];
}

View File

@@ -1,19 +1,24 @@
<template>
<BaseModal v-model="modal">
<template #title> Create Item </template>
<template #title> {{ $t("components.item.create_modal.title") }} </template>
<form @submit.prevent="create()">
<LocationSelector v-model="form.location" />
<FormTextField ref="nameInput" v-model="form.name" :trigger-focus="focused" :autofocus="true" label="Item Name" />
<FormTextArea v-model="form.description" label="Item Description" />
<FormMultiselect v-model="form.labels" label="Labels" :items="labels ?? []" />
<div class="modal-action">
<div class="flex justify-center">
<div>
<label for="photo" class="btn">{{ $t("components.item.create_modal.photo_button") }}</label>
<input id="photo" type="file" accept="image/*" style="visibility: hidden" @change="previewImage" />
</div>
<BaseButton class="rounded-r-none" :loading="loading" type="submit">
<template #icon>
<MdiPackageVariant class="swap-off h-5 w-5" />
<MdiPackageVariantClosed class="swap-on h-5 w-5" />
</template>
Create
{{ $t("global.create") }}
</BaseButton>
<div class="dropdown dropdown-top">
<label tabindex="0" class="btn rounded-l-none rounded-r-xl">
@@ -21,12 +26,24 @@
</label>
<ul tabindex="0" class="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-64 right-0">
<li>
<button type="button" @click="create(false)">Create and Add Another</button>
<button type="button" @click="create(false)">{{ $t("global.create_and_add") }}</button>
</li>
</ul>
</div>
</div>
</div>
<!-- photo preview area is AFTER the create button, to avoid pushing the button below the screen on small displays -->
<div class="border-t border-gray-300 p-4">
<template v-if="form.preview">
<p class="mb-0">File name: {{ form.photo?.name }}</p>
<img
:src="form.preview"
class="h-[100px] w-full object-cover rounded-t shadow-sm border-gray-300"
alt="Uploaded Photo"
/>
</template>
</div>
</form>
<p class="text-sm text-center mt-4">
use <kbd class="kbd kbd-xs">Shift</kbd> + <kbd class="kbd kbd-xs"> Enter </kbd> to create and add another
@@ -41,6 +58,7 @@
import MdiPackageVariant from "~icons/mdi/package-variant";
import MdiPackageVariantClosed from "~icons/mdi/package-variant-closed";
import MdiChevronDown from "~icons/mdi/chevron-down";
import { AttachmentTypes } from "~~/lib/api/types/non-generated";
const props = defineProps({
modelValue: {
@@ -85,10 +103,25 @@
description: "",
color: "", // Future!
labels: [] as LabelOut[],
preview: null as string | null,
photo: null as File | null,
});
const { shift } = useMagicKeys();
function previewImage(event: Event) {
const input = event.target as HTMLInputElement;
if (input.files && input.files.length > 0) {
const reader = new FileReader();
reader.onload = e => {
form.preview = e.target?.result as string;
};
const file = input.files[0];
form.photo = file;
reader.readAsDataURL(file);
}
}
whenever(
() => modal.value,
() => {
@@ -112,6 +145,13 @@
return;
}
if (loading.value) {
toast.error("Already creating an item");
return;
}
loading.value = true;
if (shift.value) {
close = false;
}
@@ -127,16 +167,32 @@
const { error, data } = await api.items.create(out);
loading.value = false;
if (error) {
loading.value = false;
toast.error("Couldn't create item");
return;
}
toast.success("Item created");
// if the photo was provided, upload it
if (form.photo) {
const { error } = await api.items.attachments.add(data.id, form.photo, form.photo.name, AttachmentTypes.Photo);
if (error) {
loading.value = false;
toast.error("Failed to upload Photo");
return;
}
toast.success("Photo uploaded");
}
// Reset
form.name = "";
form.description = "";
form.color = "";
form.preview = null;
form.photo = null;
focused.value = false;
loading.value = false;

View File

@@ -29,7 +29,7 @@
<template>
<section>
<BaseSectionHeader class="mb-2 flex justify-between items-center">
Items
{{ $t("components.item.view.selectable.items") }}
<template #description>
<div v-if="!viewSet" class="dropdown dropdown-hover dropdown-left">
<label tabindex="0" class="btn btn-ghost m-1">
@@ -39,13 +39,13 @@
<li>
<button @click="setViewPreference('card')">
<MdiCardTextOutline class="h-5 w-5" />
Card
{{ $t("components.item.view.selectable.card") }}
</button>
</li>
<li>
<button @click="setViewPreference('table')">
<MdiTable class="h-5 w-5" />
Table
{{ $t("components.item.view.selectable.table") }}
</button>
</li>
</ul>
@@ -59,7 +59,7 @@
<template v-else>
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
<ItemCard v-for="item in items" :key="item.id" :item="item" />
<div class="first:block hidden text-lg">No Items to Display</div>
<div class="first:block hidden text-lg">{{ $t("components.item.view.selectable.no_items") }}</div>
</div>
</template>
</section>

View File

@@ -1,5 +1,5 @@
<template>
<BaseCard>
<BaseCard class="overflow-clip">
<table class="table w-full">
<thead>
<tr>
@@ -63,7 +63,14 @@
</tr>
</tbody>
</table>
<div v-if="hasPrev || hasNext" class="border-t p-3 justify-end flex">
<div v-if="items.length > 10" class="border-t p-3 justify-end flex gap-3">
<div class="flex items-center">Rows per page</div>
<select v-model.number="pagination.rowsPerPage" class="select select-sm select-primary">
<option :value="10">10</option>
<option :value="25">25</option>
<option :value="50">50</option>
<option :value="100">100</option>
</select>
<div class="btn-group">
<button :disabled="!hasPrev" class="btn btn-sm" @click="prev()">«</button>
<button class="btn btn-sm">Page {{ pagination.page }}</button>
@@ -97,13 +104,22 @@
] as TableHeader[];
});
const preferences = useViewPreferences();
const pagination = reactive({
descending: false,
page: 1,
rowsPerPage: 10,
rowsPerPage: preferences.value.itemsPerTablePage,
rowsNumber: 0,
});
watch(
() => pagination.rowsPerPage,
newRowsPerPage => {
preferences.value.itemsPerTablePage = newRowsPerPage;
}
);
const next = () => pagination.page++;
const hasNext = computed<boolean>(() => {
return pagination.page * pagination.rowsPerPage < props.items.length;
@@ -189,4 +205,20 @@
}
</script>
<style scoped></style>
<style scoped>
:where(.table *:first-child) :where(*:first-child) :where(th, td):first-child {
border-top-left-radius: 0px;
}
:where(.table *:first-child) :where(*:first-child) :where(th, td):last-child {
border-top-right-radius: 0px;
}
:where(.table *:last-child) :where(*:last-child) :where(th, td):first-child {
border-bottom-left-radius: 0px;
}
:where(.table *:last-child) :where(*:last-child) :where(th, td):last-child {
border-bottom-right-radius: 0px;
}
</style>

View File

@@ -1,6 +1,6 @@
<template>
<BaseModal v-model="modal">
<template #title> Create Label </template>
<template #title>{{ $t("components.label.create_modal.title") }}</template>
<form @submit.prevent="create()">
<FormTextField
ref="locationNameRef"
@@ -12,14 +12,14 @@
<FormTextArea v-model="form.description" label="Label Description" />
<div class="modal-action">
<div class="flex justify-center">
<BaseButton class="rounded-r-none" :loading="loading" type="submit"> Create </BaseButton>
<BaseButton class="rounded-r-none" :loading="loading" type="submit"> {{ $t("global.create") }} </BaseButton>
<div class="dropdown dropdown-top">
<label tabindex="0" class="btn rounded-l-none rounded-r-xl">
<MdiChevronDown class="h-5 w-5" />
</label>
<ul tabindex="0" class="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-64 right-0">
<li>
<button type="button" @click="create(false)">Create and Add Another</button>
<button type="button" @click="create(false)">{{ $t("global.create_and_add") }}</button>
</li>
</ul>
</div>
@@ -71,6 +71,12 @@
const { shift } = useMagicKeys();
async function create(close = true) {
if (loading.value) {
toast.error("Already creating a label");
return;
}
loading.value = true;
if (shift.value) {
close = false;
}

View File

@@ -1,6 +1,6 @@
<template>
<BaseModal v-model="modal">
<template #title> Create Location </template>
<template #title>{{ $t("components.location.create_modal.title") }}</template>
<form @submit.prevent="create()">
<FormTextField
ref="locationNameRef"
@@ -13,14 +13,14 @@
<LocationSelector v-model="form.parent" />
<div class="modal-action">
<div class="flex justify-center">
<BaseButton class="rounded-r-none" type="submit" :loading="loading"> Create </BaseButton>
<BaseButton class="rounded-r-none" type="submit" :loading="loading">{{ $t("global.create") }}</BaseButton>
<div class="dropdown dropdown-top">
<label tabindex="0" class="btn rounded-l-none rounded-r-xl">
<MdiChevronDown class="h-5 w-5" />
</label>
<ul tabindex="0" class="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-64 right-0">
<li>
<button type="button" @click="create(false)">Create and Add Another</button>
<button type="button" @click="create(false)">{{ $t("global.create_and_add") }}</button>
</li>
</ul>
</div>
@@ -73,6 +73,10 @@
const { shift } = useMagicKeys();
async function create(close = true) {
if (loading.value) {
toast.error("Already creating a location");
return;
}
loading.value = true;
if (shift.value) {

View File

@@ -1,11 +1,11 @@
<template>
<BaseModal v-model="isRevealed" readonly @cancel="cancel(false)">
<template #title> Confirm </template>
<template #title> {{ $t("global.confirm") }} </template>
<div>
<p>{{ text }}</p>
</div>
<div class="modal-action">
<BaseButton type="submit" @click="confirm(true)"> Confirm </BaseButton>
<BaseButton type="submit" @click="confirm(true)"> {{ $t("global.confirm") }} </BaseButton>
</div>
</BaseModal>
</template>

View File

@@ -45,6 +45,7 @@
options: any[];
display?: string;
modelValue: any[];
uniqueField: string;
};
const btn = ref<HTMLButtonElement>();
@@ -75,6 +76,7 @@
label: "",
display: "name",
modelValue: () => [],
uniqueField: "id",
});
const len = computed(() => {
@@ -95,9 +97,9 @@
const unselected = computed(() => {
return props.options.filter(o => {
if (searchFold.value.length > 0) {
return o[props.display].toLowerCase().includes(searchFold.value) && !selected.value.includes(o);
return o[props.display].toLowerCase().includes(searchFold.value) && selected.value.every(s => s[props.uniqueField] !== o[props.uniqueField]);
}
return !selected.value.includes(o);
return selected.value.every(s => s[props.uniqueField] !== o[props.uniqueField]);
});
});
</script>

View File

@@ -7,7 +7,7 @@
</slot>
<div tabindex="0" class="card compact dropdown-content shadow-lg bg-base-100 rounded-box w-64">
<div class="card-body">
<h2 class="text-center">Page URL</h2>
<h2 class="text-center">{{ $t("components.global.page_qr_code.page_url") }}</h2>
<img :src="getQRCodeUrl()" />
</div>
</div>

View File

@@ -1,6 +1,6 @@
<template>
<div class="py-4">
<p class="text-sm">Password Strength: {{ message }}</p>
<p class="text-sm">{{ $t("components.global.password_score.password_strength") }}: {{ message }}</p>
<progress
class="progress w-full progress-bar"
:value="score"

View File

@@ -1,7 +1,7 @@
<template>
<div class="stats bg-neutral shadow rounded-md">
<div class="stat text-neutral-content text-center space-y-1 p-3">
<div class="stat-title">{{ title }}</div>
<div class="stat-title text-neutral-content">{{ title }}</div>
<div class="stat-value text-2xl">
<Currency v-if="type === 'currency'" :amount="value" />
<template v-if="type === 'number'">{{ value }}</template>
@@ -27,8 +27,4 @@
});
</script>
<style>
[data-theme="homebox"] .stat-title {
color: hsl(0 0% 90/0.6);
}
</style>
<style></style>

View File

@@ -9,6 +9,7 @@ export type LocationViewPreferences = {
editorAdvancedView: boolean;
itemDisplayView: ViewType;
theme: DaisyTheme;
itemsPerTablePage: number;
};
/**
@@ -24,6 +25,7 @@ export function useViewPreferences(): Ref<LocationViewPreferences> {
editorAdvancedView: false,
itemDisplayView: "card",
theme: "homebox",
itemsPerTablePage: 10,
},
{ mergeDefaults: true }
);

View File

@@ -32,7 +32,7 @@ export function useRouteQuery(q: string, def: any): WritableComputedRef<any> {
case "string":
return computed({
get: () => {
const qv = route.query[q];
const qv = first.value;
if (Array.isArray(qv)) {
return qv[0];
}

View File

@@ -29,6 +29,12 @@
</div>
<slot></slot>
<footer v-if="status" class="text-center w-full bottom-0 pb-4 bg-base-300 text-secondary-content">
<p class="text-center text-sm">
{{ $t("global.version", { version: status.build.version }) }} ~
{{ $t("global.build", { build: status.build.commit }) }}
</p>
</footer>
</div>
<!-- Sidebar -->
@@ -39,7 +45,7 @@
<div class="w-60 py-5 md:py-10 bg-base-200 flex flex-grow-1 flex-col">
<div class="space-y-8">
<div class="flex flex-col items-center gap-4">
<p>Welcome, {{ username }}</p>
<p>{{ $t("global.welcome", { username: username }) }}</p>
<NuxtLink class="avatar placeholder" to="/home">
<div class="bg-base-300 text-neutral-content rounded-full w-24 p-4">
<AppLogo />
@@ -53,7 +59,7 @@
<span>
<MdiPlus class="mr-1 -ml-1" />
</span>
Create
{{ $t("global.create") }}
</label>
<ul tabindex="0" class="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-40">
<li v-for="btn in dropdown" :key="btn.name">
@@ -83,7 +89,9 @@
</div>
<!-- Bottom -->
<button class="mt-auto mx-2 hover:bg-base-300 p-3 rounded-btn" @click="logout">Sign Out</button>
<button class="mt-auto mx-2 hover:bg-base-300 p-3 rounded-btn" @click="logout">
{{ $t("global.sign_out") }}
</button>
</div>
</div>
</div>
@@ -101,12 +109,17 @@
import MdiMagnify from "~icons/mdi/magnify";
import MdiAccount from "~icons/mdi/account";
import MdiCog from "~icons/mdi/cog";
const username = computed(() => authCtx.user?.name || "User");
const pubApi = usePublicApi();
const { data: status } = useAsyncData(async () => {
const { data } = await pubApi.status();
return data;
});
// Preload currency format
useFormatCurrency();
const modals = reactive({
item: false,
location: false,

80
frontend/locales/ca.json Normal file
View File

@@ -0,0 +1,80 @@
{
"components": {
"app": {
"import_dialog": {
"title": "Importa un fitxer CSV",
"upload": "Puja",
"description": "Importa un fitxer CSV que contingui els articles, etiquetes i ubicacions. \nConsulteu la documentació per a més informació sobre el format requerit.",
"change_warning": "El comportament de les importacions amb import_refs existents ha canviat. Si hi ha un import_refs al fitxer CSV, \nl'article s'actualitzarà amb els valors del fitxer CSV."
}
},
"item": {
"create_modal": {
"title": "Crea un article",
"photo_button": "Foto 📷"
},
"view": {
"selectable": {
"card": "Targeta",
"items": "Articles",
"no_items": "No hi ha articles a mostrar",
"table": "Taula"
}
}
},
"label": {
"create_modal": {
"title": "Crea una etiqueta"
}
},
"location": {
"create_modal": {
"title": "Crea una ubicació"
}
},
"global": {
"page_qr_code": {
"page_url": "URL de la pàgina"
},
"password_score": {
"password_strength": "Força de la contrasenya"
}
}
},
"global": {
"build": "Construcció: { build }",
"confirm": "Confirma",
"create": "Crea",
"create_and_add": "Crea i afegeix-ne un altre",
"created": "Creat",
"email": "Correu electrònic",
"follow_dev": "Segueix al desenvolupador",
"github": "Projecte de GitHub",
"items": "Articles",
"join_discord": "Uniu-vos a Discord",
"locations": "Ubicacions",
"name": "Nom",
"read_docs": "Llegiu la documentació",
"search": "Cerca",
"sign_out": "Tanca la sessió",
"submit": "Envia",
"welcome": "Us donem la benvinguda, { username }",
"labels": "Etiquetes",
"password": "Contrasenya",
"version": "Versió {version}"
},
"index": {
"joining_group": "Us uniu a un grup existent!",
"dont_join_group": "Voleu unir-vos al grup?",
"login": "Inici de sessió",
"disabled_registration": "El registre és desactivat",
"register": "Registra-m'hi",
"remember_me": "Recorda'm",
"set_email": "Quin és el seu correu electrònic?",
"set_name": "Com us dieu?",
"set_password": "Definiu la contrasenya"
},
"items": {
"negate_labels": "Nega les etiquetes seleccionades"
}
}

128
frontend/locales/de.json Normal file
View File

@@ -0,0 +1,128 @@
{
"components": {
"app": {
"import_dialog": {
"change_warning": "Das Verhalten beim Importieren vorhandener import_refs hat sich geändert. Wenn ein import_ref in der CSV-Datei vorhanden ist, \nwird der Gegenstand mit den Werten in der CSV-Datei aktualisiert.",
"description": "Importiere eine CSV-Datei, die deine Gegenstände, Etiketten und Standorte enthält. Schau in die Dokumentation für weitere Informationen\nzum erforderlichen Format.",
"title": "CSV-Datei importieren",
"upload": "Hochladen"
}
},
"global": {
"page_qr_code": {
"page_url": "Seiten-URL"
},
"password_score": {
"password_strength": "Passwortstärke"
}
},
"item": {
"create_modal": {
"title": "Gegenstand erstellen",
"photo_button": "Foto 📷"
},
"view": {
"selectable": {
"card": "Karte",
"items": "Gegenstände",
"no_items": "Keine Gegenstände anzuzeigen",
"table": "Tabelle"
}
}
},
"label": {
"create_modal": {
"title": "Etikett erstellen"
}
},
"location": {
"create_modal": {
"title": "Standort erstellen"
}
}
},
"global": {
"build": "Build: { build }",
"confirm": "Bestätigen",
"create": "Erstellen",
"create_and_add": "Erstellen und weiteren hinzufügen",
"created": "Erstellt",
"email": "E-Mail",
"follow_dev": "Dem Entwickler folgen",
"github": "GitHub-Projekt",
"items": "Gegenstände",
"join_discord": "Discord beitreten",
"labels": "Etiketten",
"locations": "Lagerorte",
"name": "Name",
"password": "Passwort",
"read_docs": "Dokumentation lesen",
"search": "Suche",
"sign_out": "Abmelden",
"submit": "Einreichen",
"version": "Version: { version }",
"welcome": "Willkommen, { username }"
},
"index": {
"disabled_registration": "Registrierung deaktiviert",
"dont_join_group": "Möchtest du nicht einer Gruppe beitreten?",
"joining_group": "Du trittst einer bereits bestehenden Gruppe bei!",
"login": "Anmelden",
"register": "Registrieren",
"remember_me": "Angemeldet bleiben",
"set_email": "Was ist deine E-Mail?",
"set_name": "Wie heißt du?",
"set_password": "Setze dein Passwort",
"tagline": "Verfolgen, Organisieren und Verwalten deiner Sachen."
},
"items": {
"add": "Hinzufügen",
"created_at": "Erstellt am",
"custom_fields": "Benutzerdefinierte Felder",
"field_selector": "Feldauswahl",
"field_value": "Feldwert",
"first": "Erste",
"include_archive": "Archivierte Elemente einschließen",
"last": "Letzte",
"negate_labels": "Ausgewählte Etiketten negieren",
"next_page": "Nächste Seite",
"no_results": "Keine Elemente gefunden",
"options": "Optionen",
"order_by": "Sortieren nach",
"pages": "Seite { page } von { totalPages }",
"prev_page": "Vorherige Seite",
"query_id": "Abfrage Asset-ID-Nummer: { id }",
"reset_search": "Suche zurücksetzen",
"results": "{ total } Ergebnisse",
"tip_1": "Standort- und Etikettenfilter verwenden die 'ODER'-Operation. Wenn mehr als eines ausgewählt ist, wird\n nur eines für eine Übereinstimmung benötigt.",
"tip_2": "Suchen, die mit '#' beginnen, fragen nach einer Asset-ID (Beispiel '#000-001')",
"tip_3": "Feldfilter verwenden die 'ODER'-Operation. Wenn mehr als eines ausgewählt ist, wird nur eines\n für eine Übereinstimmung benötigt.",
"tips": "Tipps",
"tips_sub": "Suchtipps",
"updated_at": "Aktualisiert am"
},
"profile": {
"active": "Aktiv",
"change_password": "Passwort ändern",
"currency_format": "Währungsformat",
"current_password": "Aktuelles Passwort",
"delete_account": "Konto löschen",
"delete_account_sub": "Lösche dein Konto und alle zugehörigen Daten. Dies kann nicht rückgängig gemacht werden.",
"enabled": "Aktiviert",
"gen_invite": "Einladungslink generieren",
"group_settings": "Gruppeneinstellungen",
"group_settings_sub": "Geteilte Gruppeneinstellungen. Möglicherweise musst du die Seite neu laden, damit einige Einstellungen wirksam werden.",
"inactive": "Inaktiv",
"new_password": "Neues Passwort",
"notifier_modal": "{ type, select, true {Bearbeiten} false {Erstellen} other {Andere}} Notifier",
"notifiers": "Melder",
"notifiers_sub": "Erhalte Benachrichtigungen über bevorstehende Wartungserinnerungen",
"test": "Test",
"theme_settings": "Themes",
"theme_settings_sub": "Theme-Einstellungen werden im lokalen Speicher deines Browsers gespeichert. Du kannst das Theme jederzeit ändern. Wenn du Probleme hast, dein Theme einzustellen, versuche, die Seite neu zu laden.",
"update_group": "Gruppe aktualisieren",
"url": "URL",
"user_profile": "Benutzerprofil",
"user_profile_sub": "Lade Benutzer ein und verwalte dein Konto."
}
}

128
frontend/locales/en.json Normal file
View File

@@ -0,0 +1,128 @@
{
"components": {
"app": {
"import_dialog": {
"change_warning": "Behavior for imports with existing import_refs has changed. If an import_ref is present in the CSV file, the \nitem will be updated with the values in the CSV file.",
"description": "Import a CSV file containing your items, labels, and locations. See documentation for more information on the \nrequired format.",
"title": "Import CSV File",
"upload": "Upload"
}
},
"global": {
"page_qr_code": {
"page_url": "Page URL"
},
"password_score": {
"password_strength": "Password Strength"
}
},
"item": {
"create_modal": {
"title": "Create Item",
"photo_button": "Photo \uD83D\uDCF7"
},
"view": {
"selectable": {
"card": "Card",
"items": "Items",
"no_items": "No Items to Display",
"table": "Table"
}
}
},
"label": {
"create_modal": {
"title": "Create Label"
}
},
"location": {
"create_modal": {
"title": "Create Location"
}
}
},
"global": {
"build": "Build: { build }",
"confirm": "Confirm",
"create": "Create",
"create_and_add": "Create and Add Another",
"created": "Created",
"email": "Email",
"follow_dev": "Follow the Developer",
"github": "GitHub Project",
"items": "Items",
"join_discord": "Join the Discord",
"labels": "Labels",
"locations": "Locations",
"name": "Name",
"password": "Password",
"read_docs": "Read the Docs",
"search": "Search",
"sign_out": "Sign Out",
"submit": "Submit",
"version": "Version: { version }",
"welcome": "Welcome, { username }"
},
"index": {
"disabled_registration": "Registration Disabled",
"dont_join_group": "Don't want to join a group?",
"joining_group": "You're Joining an Existing Group!",
"login": "Login",
"register": "Register",
"remember_me": "Remember Me",
"set_email": "What's your email?",
"set_name": "What's your name?",
"set_password": "Set your password",
"tagline": "Track, Organize, and Manage your Things."
},
"items": {
"add": "Add",
"created_at": "Created At",
"custom_fields": "Custom Fields",
"field_selector": "Field Selector",
"field_value": "Field Value",
"first": "First",
"include_archive": "Include Archived Items",
"last": "Last",
"negate_labels": "Negate Selected Labels",
"next_page": "Next Page",
"no_results": "No Items Found",
"options": "Options",
"order_by": "Order By",
"pages": "Page { page } of { totalPages }",
"prev_page": "Previous Page",
"query_id": "Querying Asset ID Number: { id }",
"reset_search": "Reset Search",
"results": "{ total } Results",
"tip_1": "Location and label filters use the 'OR' operation. If more than one is selected only one will be\n required for a match.",
"tip_2": "Searches prefixed with '#'' will query for a asset ID (example '#000-001')",
"tip_3": "Field filters use the 'OR' operation. If more than one is selected only one will be required for a\n match.",
"tips": "Tips",
"tips_sub": "Search Tips",
"updated_at": "Updated At"
},
"profile": {
"active": "Active",
"change_password": "Change Password",
"currency_format": "Currency Format",
"current_password": "Current Password",
"delete_account": "Delete Account",
"delete_account_sub": "Delete your account and all its associated data. This can not be undone.",
"enabled": "Enabled",
"gen_invite": "Generate Invite Link",
"group_settings": "Group Settings",
"group_settings_sub": "Shared Group Settings. You may need to refresh your browser for some settings to apply.",
"inactive": "Inactive",
"new_password": "New Password",
"notifier_modal": "{ type, select, true {Edit} false {Create} other {Other}} Notifier",
"notifiers": "Notifiers",
"notifiers_sub": "Get notifications for upcoming maintenance reminders",
"test": "Test",
"theme_settings": "Theme Settings",
"theme_settings_sub": "Theme settings are stored in your browser's local storage. You can change the theme at any time. If you're\n having trouble setting your theme try refreshing your browser.",
"update_group": "Update Group",
"url": "URL",
"user_profile": "User Profile",
"user_profile_sub": "Invite users, and manage your account."
}
}

128
frontend/locales/es.json Normal file
View File

@@ -0,0 +1,128 @@
{
"components": {
"app": {
"import_dialog": {
"change_warning": "Se ha modificado el comportamiento de las importaciones con import_refs existentes. Si existe una import_ref en el archivo CSV, el \nelemento se actualizará con los valores del archivo CSV.",
"description": "Importa un archivo CSV que contenga tus elementos, etiquetas y ubicaciones. Consulta la documentación para obtener más información sobre el \nformato requerido.",
"title": "Importar Archivo CSV",
"upload": "Subir"
}
},
"global": {
"page_qr_code": {
"page_url": "URL de página"
},
"password_score": {
"password_strength": "Seguridad de la contraseña"
}
},
"item": {
"create_modal": {
"title": "Crear Elemento",
"photo_button": "Foto 📷"
},
"view": {
"selectable": {
"card": "Tarjeta",
"items": "Elementos",
"no_items": "No hay elementos para mostrar",
"table": "Tabla"
}
}
},
"label": {
"create_modal": {
"title": "Crear Etiqueta"
}
},
"location": {
"create_modal": {
"title": "Crear Ubicación"
}
}
},
"global": {
"build": "Compilación: { build }",
"confirm": "Confirmar",
"create": "Crear",
"created": "Creado",
"email": "Email",
"github": "Proyecto GitHub",
"items": "Elementos",
"join_discord": "Únete al Discord",
"labels": "Etiquetas",
"locations": "Ubicaciones",
"name": "Nombre",
"password": "Contraseña",
"read_docs": "Lee la Documentación",
"search": "Buscar",
"sign_out": "Cerrar Sesión",
"submit": "Enviar",
"welcome": "Bienvenido/a, { username }",
"create_and_add": "Crear y Añadir Otro",
"follow_dev": "Seguir al Desarrollador",
"version": "Versión: { version }"
},
"index": {
"disabled_registration": "Registro Desactivado",
"dont_join_group": "¿No quieres unirte a un grupo?",
"login": "Iniciar sesión",
"register": "Registrarse",
"remember_me": "Recuérdame",
"set_email": "¿Cuál es tu email?",
"set_name": "¿Cómo te llamas?",
"set_password": "Establece tu contraseña",
"tagline": "Rastrea, Organiza y Gestiona tus Cosas.",
"joining_group": "¡Te estás uniendo a un grupo existente!"
},
"items": {
"add": "Añadir",
"created_at": "Creado El",
"custom_fields": "Campos Personalizados",
"field_selector": "Selector de Campo",
"field_value": "Valor del Campo",
"first": "Primer",
"include_archive": "Incluir Elementos Archivados",
"last": "Último",
"negate_labels": "Negar Etiquetas Seleccionadas",
"next_page": "Siguiente Página",
"no_results": "No se Encontraron Elementos",
"options": "Opciones",
"order_by": "Ordenar Por",
"pages": "Página { page } de { totalPages }",
"prev_page": "Anterior Página",
"query_id": "Consultar Número ID del Activo: { id }",
"reset_search": "Restablecer Búsqueda",
"results": "{ total } Resultados",
"tip_2": "Las búsquedas precedidas de \"#\" buscarán un ID de activo (por ejemplo, \"#000-001\")",
"tip_3": "Los filtros de campo utilizan el operador \"OR\". Si se selecciona más de uno, sólo se requerirá uno para una\n coincidencia.",
"tip_1": "Los filtros de ubicación y etiquetas utilizan el operador \"OR\". Si se selecciona más de uno, sólo uno será\n necesario para obtener una coincidencia.",
"tips": "Sugerencias",
"tips_sub": "Sugerencias de Búsqueda",
"updated_at": "Actualizado El"
},
"profile": {
"active": "Activo",
"change_password": "Cambiar Contraseña",
"currency_format": "Formato Divisa",
"current_password": "Contraseña Actual",
"delete_account": "Eliminar Cuenta",
"enabled": "Habilitado",
"gen_invite": "Generar Enlace de Invitación",
"group_settings": "Ajustes de Grupo",
"group_settings_sub": "Configuración de Grupo Compartido. Es posible que tengas que actualizar tu navegador para que se apliquen algunos ajustes.",
"inactive": "Inactivo",
"new_password": "Nueva Contraseña",
"notifiers": "Notificaciones",
"notifiers_sub": "Recibe notificaciones de los próximos recordatorios de mantenimiento",
"test": "Probar",
"theme_settings": "Ajustes de Tema",
"update_group": "Actualizar Grupo",
"url": "URL",
"user_profile": "Perfil de Usuario",
"user_profile_sub": "Invita a usuarios y gestiona tu cuenta.",
"delete_account_sub": "Elimina tu cuenta y todos sus datos asociados. Esto no se puede deshacer.",
"notifier_modal": "{ type, select, true {Edit} false {Create} other {Other}} Notificación",
"theme_settings_sub": "La configuración del tema se guarda en el almacenamiento local de tu navegador. Puedes cambiar el tema en cualquier momento. Si tienes\n problemas para configurar el tema, prueba a actualizar el navegador."
}
}

128
frontend/locales/fr.json Normal file
View File

@@ -0,0 +1,128 @@
{
"global": {
"email": "Courriel",
"read_docs": "Lire la documentation",
"password": "Mot de passe",
"github": "Projet GitHub",
"join_discord": "Rejoindre le Discord",
"follow_dev": "Suivre le développeur",
"version": "Version : { version }",
"build": "Assemblage : { build }",
"create": "Créer",
"submit": "Soumettre",
"create_and_add": "Créer et en ajouter un autre",
"confirm": "Confirmer",
"sign_out": "Déconnexion",
"welcome": "Bienvenue, { username }",
"created": "Créé",
"labels": "Étiquettes",
"locations": "Emplacements",
"name": "Nom",
"search": "Rechercher",
"items": "Articles"
},
"index": {
"set_email": "Quel est vôtre courriel?",
"set_name": "Quel est votre nom?",
"login": "Se connecter",
"register": "S'enregistrer",
"remember_me": "Se souvenir de moi",
"set_password": "Définir votre mot de passe",
"dont_join_group": "Vous ne voulez pas joindre un groupe ?",
"tagline": "Répertoriez, organisez et gérez vos affaires.",
"disabled_registration": "Les inscriptions sont désactivées",
"joining_group": "Vous rejoignez un groupe existant!"
},
"components": {
"label": {
"create_modal": {
"title": "Créer une étiquette"
}
},
"item": {
"view": {
"selectable": {
"items": "Articles",
"no_items": "Nb. darticles à afficher",
"table": "Tableau",
"card": "Carte"
}
},
"create_modal": {
"title": "Créer un article",
"photo_button": "Photo 📷"
}
},
"location": {
"create_modal": {
"title": "Créer un emplacement"
}
},
"global": {
"password_score": {
"password_strength": "Solidité du mot de passe"
},
"page_qr_code": {
"page_url": "URL de la page"
}
},
"app": {
"import_dialog": {
"title": "Importer un fichier CSV",
"upload": "Téléverser",
"description": "Importer un fichier CSV contenant vos articles, étiquettes, et emplacements. Voir la documentation pour plus dinformations sur le format requis.",
"change_warning": "Le comportement lors dimportations avec des import_ref existants a changé. Si une valeur pour import_ref est présente dans le fichier CSV, alors lélément sera mis à jour avec celle-ci."
}
}
},
"profile": {
"notifiers_sub": "Recevez des notifications pour vous prévenir des prochaines maintenances.",
"active": "Actif",
"enabled": "Activé",
"test": "Test",
"current_password": "Mot de passe actuel",
"new_password": "Nouveau mot de passe",
"change_password": "Changer de mot de passe",
"inactive": "Inactif",
"notifiers": "Notifications",
"gen_invite": "Générer un lien dinvitation",
"user_profile": "Profil utilisateur",
"user_profile_sub": "Gérez votre compte et invitez des utilisateurs.",
"url": "URL",
"delete_account": "Effacer le compte",
"delete_account_sub": "Supprimer le compte et toutes ses données. Aucune récupération possible.",
"group_settings": "Paramètres du groupe",
"group_settings_sub": "Paramètres du groupe partagé. Il peut être nécessaire de recharger la page pour quils deviennent effectifs.",
"currency_format": "Format de la devise",
"update_group": "Mettre à jour le groupe",
"theme_settings": "Paramètres du thème",
"theme_settings_sub": "Les paramètres du thème sont stockés dans le navigateur. Vous pouvez les changer à tout moment. Si vous\nrencontrez des problèmes, il est conseillé de rafraichir la page.",
"notifier_modal": "Notifications { type, select, true {Edit} false {Create} other {Other}}"
},
"items": {
"add": "Ajouter",
"created_at": "Créé à",
"custom_fields": "Champs personnalisés",
"field_selector": "Sélecteur de champ",
"field_value": "Valeur du champ",
"first": "Premier",
"include_archive": "Inclure les éléments archivés",
"last": "Dernier",
"negate_labels": "Négliger les étiquettes sélectionnées",
"next_page": "Page suivante",
"no_results": "Aucun élément trouvé",
"options": "Options",
"order_by": "Trier par",
"pages": "Page { page } sur { totalPages }",
"prev_page": "Page précédente",
"query_id": "Interrogation du numéro d'identification de l'actif : { id }",
"reset_search": "Réinitialiser la recherche",
"tip_1": "Les filtres demplacement et détiquette utilisent lopérateur « OU ».\nUn seul des filtres na besoin de correspondre pour retourner un résultat.",
"tip_2": "Les recherches préfixées par '#'' rechercheront un ID d'actif (exemple '#000-001')",
"tip_3": "Les filtres de champ utilisent lopérateur « OU ».\nUn seul des filtres na besoin de correspondre pour retourner un résultat.",
"tips": "Conseils",
"tips_sub": "Conseils pour la recherche",
"updated_at": "Mis à jour le",
"results": "{ total } résultats"
}
}

127
frontend/locales/it.json Normal file
View File

@@ -0,0 +1,127 @@
{
"components": {
"app": {
"import_dialog": {
"change_warning": "Il comportamento per le importazioni con import_ref esistenti è cambiato. Se un import_ref è presente nel file CSV,\n l'elemento verrà aggiornato con i valori presenti nel file CSV.",
"description": "Importa un file CSV contenente gli oggetti, le etichette e le posizioni. Vedi la documentazione per ulteriori informazioni sul \nformato richiesto.",
"title": "Importa File CSV",
"upload": "Carica"
}
},
"global": {
"page_qr_code": {
"page_url": "URL della Pagina"
},
"password_score": {
"password_strength": "Complessità della Password"
}
},
"item": {
"create_modal": {
"title": "Crea Oggetto"
},
"view": {
"selectable": {
"card": "Scheda",
"items": "Oggetti",
"no_items": "Nessun Oggetto da Visualizzare",
"table": "Tabella"
}
}
},
"label": {
"create_modal": {
"title": "Crea Etichetta"
}
},
"location": {
"create_modal": {
"title": "Crea Posizione"
}
}
},
"global": {
"build": "Build: { build }",
"confirm": "Conferma",
"create": "Crea",
"create_and_add": "Crea e aggiungi un altro",
"created": "Creato",
"email": "Email",
"follow_dev": "Segui lo Sviluppatore",
"github": "Progetto GitHub",
"items": "Articoli",
"join_discord": "Unisciti a Discord",
"labels": "Etichette",
"locations": "Posizioni",
"name": "Nome",
"password": "Password",
"read_docs": "Leggi la Documentazione",
"search": "Cerca",
"sign_out": "Disconnetti",
"submit": "Invia",
"version": "Versione: { version }",
"welcome": "Benvenuto, { username }"
},
"index": {
"disabled_registration": "Registrazione Disabilitata",
"dont_join_group": "Non vuoi unirti a un gruppo?",
"joining_group": "Stai unendoti a un gruppo esistente!",
"login": "Accedi",
"register": "Registrati",
"remember_me": "Ricordami",
"set_email": "Qual è la tua email?",
"set_name": "Come ti chiami?",
"set_password": "Imposta la tua password",
"tagline": "Tieni traccia, Organizza e Gestisci le tue Cose."
},
"items": {
"add": "Aggiungi",
"created_at": "Creato Il",
"custom_fields": "Campi Personalizzati",
"field_selector": "Selettore di Campo",
"field_value": "Valore del Campo",
"first": "Primo",
"include_archive": "Includi Articoli Archiviati",
"last": "Ultimo",
"negate_labels": "Negare Etichette Selezionate",
"next_page": "Pagina Successiva",
"no_results": "Nessun Articolo Trovato",
"options": "Opzioni",
"order_by": "Ordina Per",
"pages": "Pagina { page } di { totalPages }",
"prev_page": "Pagina Precedente",
"query_id": "ID dell'Asset in Ricerca: { id }",
"reset_search": "Reimposta Ricerca",
"results": "{ total } Risultati",
"tip_1": "I filtri di posizione ed etichetta utilizzano l'operazione 'OR'. Se ne viene selezionato più\n di uno, ne sarà richiesto solo uno per una corrispondenza.",
"tip_2": "Le ricerche con prefisso '#' cercheranno un ID asset (esempio '#000-001')",
"tip_3": "I filtri di campo utilizzano l'operazione 'OR'. Se ne viene selezionato più di uno, ne sarà\n richiesto solo uno per una corrispondenza.",
"tips": "Suggerimenti",
"tips_sub": "Suggerimenti per la Ricerca",
"updated_at": "Aggiornato Il"
},
"profile": {
"active": "Attivo",
"change_password": "Cambia Password",
"currency_format": "Formato Valuta",
"current_password": "Password Corrente",
"delete_account": "Elimina Account",
"delete_account_sub": "Elimina il tuo account e tutti i dati associati. Questa operazione non può essere annullata.",
"enabled": "Abilitato",
"gen_invite": "Genera Link di Invito",
"group_settings": "Impostazioni Gruppo",
"group_settings_sub": "Impostazioni Gruppo Condivise. Potrebbe essere necessario aggiornare il browser affinché alcune impostazioni vengano applicate.",
"inactive": "Inattivo",
"new_password": "Nuova Password",
"notifier_modal": "{ type, select, true {Modifica} false {Crea} other {Altro}} Notifier",
"notifiers": "Notifiche",
"notifiers_sub": "Ricevi notifiche per i prossimi promemoria di manutenzione",
"test": "Test",
"theme_settings": "Impostazioni Tema",
"theme_settings_sub": "Le impostazioni del tema sono memorizzate nella memoria locale del tuo browser. Puoi cambiare il tema \nin qualsiasi momento. Se hai problemi a impostare il tuo tema, prova a ricaricare la pagina.",
"update_group": "Aggiorna Gruppo",
"url": "URL",
"user_profile": "Profilo Utente",
"user_profile_sub": "Invita utenti e gestisci il tuo account."
}
}

128
frontend/locales/nl.json Normal file
View File

@@ -0,0 +1,128 @@
{
"global": {
"version": "Versie: { version }",
"github": "GitHub Project",
"join_discord": "Sluit je aan bij de Discord",
"follow_dev": "Volg de ontwikkelaar",
"read_docs": "Lees de documentatie",
"email": "E-mail",
"submit": "Indienen",
"confirm": "Bevestigen",
"create": "Maken",
"create_and_add": "Maak en voeg nog een toe",
"password": "Wachtwoord",
"build": "Bouw: { build }",
"created": "Gemaakt",
"welcome": "Welkom, { username }",
"sign_out": "Log uit",
"labels": "etiketten",
"locations": "Locaties",
"name": "Naam",
"items": "artikelen",
"search": "Zoeken"
},
"index": {
"disabled_registration": "Registratie uitgeschakeld",
"login": "Log in",
"register": "Registreer",
"remember_me": "Onthoud mij",
"set_email": "Wat is je mailadres?",
"set_password": "Stel je wachtwoord in",
"set_name": "Wat is je naam?",
"joining_group": "Je neemt deel aan een bestaande groep!",
"tagline": "Volg, Organiseer en beheer je dingen.",
"dont_join_group": "Wil je niet aan een groep deelnemen?"
},
"components": {
"global": {
"password_score": {
"password_strength": "Wachtwoord sterkte"
},
"page_qr_code": {
"page_url": "Pagina URL"
}
},
"app": {
"import_dialog": {
"title": "Importeer CSV bestand",
"change_warning": "Gedrag voor importeren met bestaande import_refs is veranderd. Als een import_refs reeds bestaat in het CSV bestand, het\nobject zal worden aangepast met de waardes van het CSV bestand.",
"upload": "Upload",
"description": "Importeer een CSV bestand met je objecten, labels en locaties. Zie documentatie voor meer informatie m.b.t.\n vereist format."
}
},
"item": {
"create_modal": {
"title": "Maak object",
"photo_button": "Foto 📷"
},
"view": {
"selectable": {
"items": "Objecten",
"card": "Kaart",
"table": "Tabel",
"no_items": "Geen objecten om te tonen"
}
}
},
"label": {
"create_modal": {
"title": "Maak label"
}
},
"location": {
"create_modal": {
"title": "Maak locatie"
}
}
},
"profile": {
"gen_invite": "Genereer Uitnodigingslink",
"notifier_modal": "{ type, select, true {Bewerk} false {Creëer} other {overig}} Notifier",
"change_password": "Verander Wachtwoord",
"current_password": "Huidig Wachtwoord",
"new_password": "Nieuw Wachtwoord",
"url": "URL",
"enabled": "ingeschakeld",
"test": "Test",
"user_profile": "Gebruikers Profiel",
"user_profile_sub": "Nodig gebruikers uit, en beheer je account.",
"active": "Actief",
"inactive": "Inactief",
"notifiers_sub": "Krijg notificaties voor opkomende onderhouds herinneringen",
"group_settings_sub": "Gedeelde groepsinstellingen",
"group_settings": "Groeps Instellingen",
"notifiers": "Notificatie",
"currency_format": "Valutanotatie",
"update_group": "Groep bijwerken",
"theme_settings": "Theme instellingen",
"theme_settings_sub": "Thema-instellingen worden opgeslagen in de lokale opslag van uw browser. Je kan deze wijzigen op elk moment. \nAls je problemen hebt met de instellingen kun je je browser verversen.",
"delete_account": "Verwijder account",
"delete_account_sub": "Verwijder je account en alle geassocieerde data. Deze actie kan niet ongedaan worden."
},
"items": {
"tip_1": "Locatie- en labelfilters gebruiken de 'OF' -werking. Als er meer dan een is geselecteerd,\nis er maar een nodig voor een overeenkomst.",
"tip_2": "Zoekopdrachten voorafgegaan door '#'' zullen om een object-ID vragen (bijvoorbeeld '#000-001')",
"tip_3": "Veldfilters gebruiken de 'OF' -bewerking. Indien meer dan 1 is geselecteerd\nzal er maar 1 nodig zijn voor een match.",
"tips_sub": "Zoektips",
"updated_at": "Bijgewerkt op",
"pages": "Pagina { page } van { totalPages }",
"prev_page": "Vorige pagina",
"query_id": "ID-nummer van object opvragen: { id }",
"reset_search": "Reset Zoeken",
"tips": "Tips",
"no_results": "Geen Items Gevonden",
"options": "Opties",
"order_by": "Sorteren op",
"results": "{ total } Resultaten",
"add": "Toevoegen",
"created_at": "Aangemaakt op",
"custom_fields": "Aangepaste velden",
"field_selector": "Veld selectie",
"field_value": "Veldwaarde",
"first": "Eerst",
"include_archive": "Inclusief gearchiveerde items",
"last": "Achternaam",
"negate_labels": "Negeer Geselecteerde Etiketten",
"next_page": "Volgende pagina"
}
}

118
frontend/locales/pl.json Normal file
View File

@@ -0,0 +1,118 @@
{
"components": {
"app": {
"import_dialog": {
"description": "Zaimportuj plik CSV zawierający Twoje przedmioty, etykiety i lokalizacje. Zobacz dokumentację, aby uzyskać \nwięcej informacji na temat wymaganego formatu.",
"title": "Zaimportuj plik CSV",
"upload": "Prześlij"
}
},
"global": {
"page_qr_code": {
"page_url": "Adres URL strony"
},
"password_score": {
"password_strength": "Siła hasła"
}
},
"item": {
"create_modal": {
"title": "Utwórz przedmiot",
"photo_button": "Zdjęcie 📷"
},
"view": {
"selectable": {
"card": "Karta",
"items": "Przedmioty",
"no_items": "Brak przedmiotów do wyświetlenia",
"table": "Tabela"
}
}
},
"label": {
"create_modal": {
"title": "Stwórz nową etykietę"
}
},
"location": {
"create_modal": {
"title": "Utwórz lokalizację"
}
}
},
"global": {
"build": "Kompilacja: {build}",
"follow_dev": "Śledź dewelopera",
"github": "Projekt na GitHubie",
"items": "Przedmioty",
"version": "Wersja:{version}",
"welcome": "Witaj, {username}",
"confirm": "Potwierdź",
"create": "Utwórz",
"create_and_add": "Utwórz i dodaj kolejny",
"created": "Utworzone",
"email": "E-mail",
"join_discord": "Dołącz do Discorda",
"labels": "Etykiety",
"locations": "Lokalizacje",
"name": "Nazwa",
"password": "Hasło",
"read_docs": "Przeczytaj dokumentację",
"search": "Wyszukaj",
"sign_out": "Wyloguj się",
"submit": "Wyślij"
},
"index": {
"set_password": "Ustaw swoje hasło",
"dont_join_group": "Nie chcesz dołączyć do grupy?",
"disabled_registration": "Rejestracja jest wyłączona",
"joining_group": "Dołączasz do istniejącej grupy!",
"login": "Zaloguj się",
"register": "Zarejestruj się",
"remember_me": "Zapamiętaj mnie",
"set_email": "Jaki jest Twój adres e-mail?",
"set_name": "Jak się nazywasz?",
"tagline": "Śledź, organizuj i zarządzaj swoimi rzeczami."
},
"items": {
"created_at": "Data utworzenia",
"field_selector": "Selektor pól",
"field_value": "Wartość pola",
"first": "Pierwszy",
"include_archive": "Uwzględnij zarchiwizowane przedmioty",
"negate_labels": "Neguj wybrane etykiety",
"no_results": "Nie znaleziono przedmiotów",
"query_id": "Zapytanie o numer identyfikacyjny zasobu: { id }",
"results": "{ total } wyniki",
"tip_3": "Filtry pól używają operacji 'LUB'. Jeśli wybrano więcej niż jeden, wystarczy jeden, \naby uzyskać dopasowanie.",
"updated_at": "Zaktualizowano",
"tip_1": "Filtry lokalizacji i etykiet używają operacji 'LUB'. Jeśli wybrano więcej niż jeden, wystarczy jeden, \naby uzyskać dopasowanie.",
"pages": "Strona {page} z {totalPages}",
"add": "Dodaj",
"custom_fields": "Pola niestandardowe",
"last": "Ostatni",
"next_page": "Następna strona",
"options": "Opcje",
"prev_page": "Poprzednia strona",
"reset_search": "Zresetuj wyszukiwanie",
"tip_2": "Wyszukiwania poprzedzone prefiksem \"#\" będą wysyłać zapytanie o identyfikator zasobu (na przykład \"#000-001\")",
"tips": "Wskazówki",
"tips_sub": "Wskazówki wyszukiwania",
"order_by": "Ułóż według"
},
"profile": {
"active": "Aktywny",
"change_password": "Zmiana hasła",
"currency_format": "Format waluty",
"delete_account": "Usuń konto",
"delete_account_sub": "Usuń swoje konto oraz wszystkie powiązane z nim dane. Tego nie można cofnąć.",
"current_password": "Bieżące hasło",
"group_settings_sub": "Ustawienia grupy udostępnione. Możesz potrzebować odświeżyć przeglądarkę, aby niektóre ustawienia zostały zastosowane.",
"inactive": "Nieaktywny",
"new_password": "Nowe hasło",
"notifier_modal": "{ type, select, true {Edit} false {Create} other {Other}} Powiadomiony",
"enabled": "Włączone",
"gen_invite": "Wygeneruj link z zaproszeniem",
"group_settings": "Ustawienia grupy"
}
}

128
frontend/locales/ru.json Normal file
View File

@@ -0,0 +1,128 @@
{
"components": {
"app": {
"import_dialog": {
"title": "Импорт CSV файла",
"change_warning": "Изменено поведение для импортов с существующими import_ref. Если в CSV-файле присутствует import_ref, \nто элемент будет обновлен значениями из CSV-файла.",
"description": "Импортируйте CSV-файл, содержащий ваши предметы, ярлыки и местоположения. Для получения информации о требуемом формате \nсм. документацию.",
"upload": "Загрузить"
}
},
"global": {
"page_qr_code": {
"page_url": "URL-адрес страницы"
},
"password_score": {
"password_strength": "Сложность пароля"
}
},
"item": {
"create_modal": {
"title": "Создать элемент",
"photo_button": "Фото 📷"
},
"view": {
"selectable": {
"items": "Элементы",
"no_items": "Нет элементов для отображения",
"table": "Таблица",
"card": "Карта"
}
}
},
"location": {
"create_modal": {
"title": "Создать локацию"
}
},
"label": {
"create_modal": {
"title": "Создать ярлык"
}
}
},
"global": {
"email": "Email",
"items": "Элементы",
"build": "Сборка: { build }",
"confirm": "Подтвердить",
"create": "Создать",
"create_and_add": "Создать и добавить еще",
"created": "Создано",
"follow_dev": "Следить за разработчиком",
"github": "Проект Github",
"labels": "Ярлыки",
"join_discord": "Присоединяйтесь к Discord",
"locations": "Локации",
"name": "Имя",
"password": "Пароль",
"read_docs": "Прочитать документацию",
"search": "Поиск",
"sign_out": "Выйти",
"submit": "Отправить",
"version": "Версия: { version }",
"welcome": "Добро пожаловать, { username }"
},
"index": {
"login": "Войти",
"register": "Зарегистрироваться",
"set_email": "Какой у вас адрес электронной почты?",
"set_name": "Как вас зовут?",
"set_password": "Установите пароль",
"tagline": "Отслеживайте, упорядочивайте и управляйте своими вещами.",
"disabled_registration": "Регистрация отключена",
"dont_join_group": "Не хотите ли вступить в группу?",
"remember_me": "Запомнить меня",
"joining_group": "Вы присоединяетесь к уже существующей группе!"
},
"items": {
"add": "Добавить",
"created_at": "Создано в",
"custom_fields": "Настраиваемые поля",
"first": "Первый",
"pages": "Страница {page} из {totalPages}",
"prev_page": "Предыдущая страница",
"reset_search": "Сбросить Поиск",
"field_selector": "Поле выбора",
"field_value": "Значение поля",
"last": "Последний",
"negate_labels": "Снять выбранные ярлыки",
"next_page": "Следующая страница",
"no_results": "Элементы не найдены",
"options": "Параметры",
"results": "{ total } Результатов",
"tips": "Подсказки",
"tips_sub": "Поисковые подсказки",
"updated_at": "Обновлено в",
"tip_3": "Фильтры по полю используют операцию «ИЛИ». Если выбрано несколько фильтров, для совпадения\n требуется только один.",
"tip_2": "Поисковые запросы с префиксом \"#\" должны включать в себя ID актива (прим. '#000-001')",
"include_archive": "Включить архивированные элементы",
"order_by": "Сортировка по",
"query_id": "Запрос идентификационного номера актива: { id }",
"tip_1": "При фильтрации по локации и по ярлыкам используется логический оператор «ИЛИ». Если выбрано несколько фильтров, то для срабатывания\n требуется лишь одно совпадение."
},
"profile": {
"new_password": "Новый пароль",
"update_group": "Обновить группу",
"active": "Активный",
"change_password": "Изменить пароль",
"currency_format": "Формат валюты",
"current_password": "Текущий пароль",
"delete_account": "Удалить аккаунт",
"group_settings": "Настройки группы",
"inactive": "Неактивный",
"notifier_modal": "{ type, select, true {Edit} false {Create} other {Other}} Уведомитель",
"notifiers": "Уведомители",
"notifiers_sub": "Получить уведомление о предстоящем обслуживании",
"test": "Тест",
"theme_settings": "Настройки темы",
"url": "URL",
"user_profile": "Профиль пользователя",
"user_profile_sub": "Приглашайте пользователей и управляйте своим аккаунтом.",
"enabled": "Активен",
"theme_settings_sub": "Настройки темы хранятся в локальном хранилище браузера. Вы можете изменить тему в любое время. Если у вас\n не удается установить тему, попробуйте перезапустить браузер.",
"delete_account_sub": "Удалить свой аккаунт и все связанные с ним данные. Это действие невозможно отменить.",
"gen_invite": "Сгенерировать ссылку-приглашение",
"group_settings_sub": "Настройки общей группы. Для применения изменений возможно потребуется перезагрузить страницу."
}
}

128
frontend/locales/sl.json Normal file
View File

@@ -0,0 +1,128 @@
{
"global": {
"follow_dev": "Sledi razvijjalcu",
"build": "Gradnja: [ build ]",
"confirm": "Potrdi",
"create": "Ustvari",
"create_and_add": "Ustvari in dodaj še enega",
"created": "Ustvarjeno",
"email": "E-pošta",
"github": "GitHub projekt",
"items": "Predmeti",
"join_discord": "Pridruži se na Discord",
"labels": "Oznake",
"locations": "Lokacije",
"name": "Naziv",
"password": "Geslo",
"read_docs": "Preberite dokumentacijo",
"search": "Iskanje",
"sign_out": "Odjava",
"submit": "Pošlji",
"version": "Verzija: { version }",
"welcome": "Dobrodošel, { username }"
},
"components": {
"app": {
"import_dialog": {
"change_warning": "Vedenje pri uvozih z obstoječimi import_refs se je spremenilo. Če je import_ref prisoten v datoteki CSV, bo\nelement posodobljen z vrednostmi v datoteki CSV.",
"description": "Uvozite datoteko CSV, ki vsebuje vaše predmete, oznake in lokacije. Poglejte si dokumentacijo za več informacij o\nzahtevani obliki.",
"title": "Uvozi CSV datoteko",
"upload": "Naloži"
}
},
"global": {
"page_qr_code": {
"page_url": "URL strani"
},
"password_score": {
"password_strength": "Moč gesla"
}
},
"item": {
"create_modal": {
"title": "Ustvari predmet",
"photo_button": "Fotografija 📷"
},
"view": {
"selectable": {
"card": "Kartica",
"items": "Predmeti",
"no_items": "Ni predmetov za prikaz",
"table": "Tabela"
}
}
},
"label": {
"create_modal": {
"title": "Ustvari oznako"
}
},
"location": {
"create_modal": {
"title": "Ustvari lokacijo"
}
}
},
"index": {
"disabled_registration": "Registracija je onemogočena",
"dont_join_group": "Se ne želite pridružiti skupini?",
"joining_group": "Pridružujete se obstoječi skupini!",
"login": "Prijava",
"register": "Registriraj se",
"remember_me": "Zapomni si me",
"set_email": "Kakšen je vaš e-poštni naslov?",
"set_password": "Nastavite geslo",
"tagline": "Sledite, organizirajte in upravljajte svoje stvari.",
"set_name": "Kako vam je ime?"
},
"items": {
"created_at": "Ustvarjeno ob",
"include_archive": "Vključi arhivirane predmete",
"last": "Zadnji",
"negate_labels": "Negiraj izbrane oznake",
"no_results": "Ni najdenih predmetov",
"options": "Možnosti",
"pages": "Stran { page } od { totalPages }",
"tip_2": "Iskanja s predpono '#' bodo iskala ID sredstva (primer '#000-001')",
"tip_3": "Filtri polj uporabljajo operacijo 'ALI'. Če je izbranih več kot eno, bo za ujemanje dovolj samo\n eno.",
"tips": "Nasveti",
"updated_at": "Posodobljeno ob",
"custom_fields": "Polje po meri",
"results": "Rezultatov: { total }",
"tip_1": "Filtri lokacij in oznak uporabljajo operacijo 'ALI'. Če je izbranih več kot ena, bo izbrana samo ena\n potrebna za ujemanje.",
"add": "Dodaj",
"field_selector": "Izbirnik polj",
"field_value": "Vrednost polja",
"first": "Prvi",
"next_page": "Naslednja stran",
"prev_page": "Prejšnja stran",
"query_id": "Poizvedovanje po identifikacijski številki sredstva: { id }",
"reset_search": "Ponastavi iskanje",
"tips_sub": "Nasveti za iskanje",
"order_by": "Razvrsti po"
},
"profile": {
"gen_invite": "Ustvari povezavo povabila",
"group_settings_sub": "Nastavitve skupine v skupni rabi. Morda boste morali osvežiti brskalnik, da bodo nekatere nastavitve veljale.",
"notifiers": "Obveščevalci",
"notifier_modal": "{ type, select, true {Edit} false {Create} other {Other}} Obveščevalec",
"notifiers_sub": "Prejemajte obvestila za prihajajoče opomnike o vzdrževanju",
"theme_settings_sub": "Nastavitve teme so shranjene v lokalni shrambi vašega brskalnika. Temo lahko kadar koli spremenite. Če\n imate težave z nastavitvijo teme, poskusite osvežiti brskalnik.",
"update_group": "Posodobi skupino",
"url": "URL",
"delete_account_sub": "Izbrišite svoj račun in vse z njim povezane podatke. Tega ni mogoče razveljaviti.",
"active": "Aktivno",
"change_password": "Sprememba gesla",
"currency_format": "Oblika valute",
"current_password": "Trenutno geslo",
"delete_account": "Izbriši račun",
"enabled": "Omogočeno",
"group_settings": "Nastavitve skupine",
"inactive": "Neaktivno",
"new_password": "Novo geslo",
"test": "Preizkus",
"theme_settings": "Nastavitve teme",
"user_profile": "Profil uporabnika",
"user_profile_sub": "Povabite uporabnike in upravljajte svoj račun."
}
}

128
frontend/locales/sv.json Normal file
View File

@@ -0,0 +1,128 @@
{
"profile": {
"notifier_modal": "{ type, select, true {Edit} false {Create} other {Other}} Anmälare",
"change_password": "Ändra Lösenord",
"current_password": "Nuvarande lösenord",
"new_password": "Nytt lösenord",
"notifiers_sub": "Få aviseringar om kommande underhållspåminnelser",
"url": "URL",
"test": "Test",
"gen_invite": "Skapa inbjudningslänk",
"user_profile": "Användarprofil",
"user_profile_sub": "Bjud in användare och hantera ditt konto.",
"active": "Aktiv",
"inactive": "Inaktiv",
"notifiers": "Notiser",
"enabled": "Aktiverad",
"currency_format": "Valuta format",
"delete_account": "Radera konto",
"delete_account_sub": "Ta bort ditt konto och alla tillhörande data. Detta kan inte ångras.",
"group_settings": "Grupp inställningar",
"group_settings_sub": "Inställningar för delad grupp. Du kan behöva uppdatera din webbläsare för att vissa inställningar ska gälla.",
"theme_settings": "Temainställningar",
"theme_settings_sub": "Temainställningar sparas i din webbläsares lokala lagring. Du kan ändra tema när du vill. Om du\nhar problem att ställa in tema, pröva att ladda om din webbläsare.",
"update_group": "Uppdatera grupp"
},
"index": {
"set_name": "Vad heter du?",
"joining_group": "Du går med i en befintlig grupp!",
"dont_join_group": "Vill du inte gå med i en grupp?",
"tagline": "Spåra, organisera och hantera dina saker.",
"disabled_registration": "Registrering avaktiverad",
"login": "Logga in",
"register": "Registrera",
"remember_me": "Kom ihåg mig",
"set_email": "Vad är din e-post?",
"set_password": "Ställ in ditt lösenord"
},
"components": {
"app": {
"import_dialog": {
"upload": "Ladda upp",
"title": "Importera CSV fil",
"description": "Importera en CSV-fil som innehåller dina föremål, etiketter och platser. Se dokumentationen för mer information om \nönskat format.",
"change_warning": "Beteendet för importer med befintliga import_refs har ändrats. Om en import_ref finns i CSV-filen, \nobjektet kommer att uppdateras med värdena i CSV-filen."
}
},
"item": {
"view": {
"selectable": {
"card": "Kort",
"items": "Föremål",
"table": "Tabell",
"no_items": "Inga föremål att visa"
}
},
"create_modal": {
"title": "Skapa föremål",
"photo_button": "Foto 📷"
}
},
"label": {
"create_modal": {
"title": "Skapa etikett"
}
},
"location": {
"create_modal": {
"title": "Skapa plats"
}
},
"global": {
"password_score": {
"password_strength": "Lösenordsstyrka"
},
"page_qr_code": {
"page_url": "Sidans URL"
}
}
},
"global": {
"build": "Byggd: { build }",
"github": "GitHub Projekt",
"join_discord": "Gå med i Discord",
"follow_dev": "Följ utvecklaren",
"read_docs": "Läs dokumenten",
"password": "Lösenord",
"email": "Epost",
"submit": "Skicka",
"confirm": "Godkänn",
"create": "Skapa",
"created": "Skapad",
"welcome": "Välkommen, { username }",
"sign_out": "Logga ut",
"create_and_add": "Skapa och lägg till en annan",
"version": "Version: { version }",
"items": "Föremål",
"labels": "Etiketter",
"locations": "Platser",
"name": "Namn",
"search": "Sök"
},
"items": {
"add": "Lägg till",
"created_at": "Skapat",
"custom_fields": "Egna fält",
"field_selector": "Fält alternativ",
"field_value": "Fält värde",
"first": "Första",
"include_archive": "Inkludera arkiverade föremål",
"last": "Sista",
"negate_labels": "Negera valda etiketter",
"next_page": "Nästa sida",
"no_results": "Inga föremål hittades",
"options": "Alternativ",
"order_by": "Ordning via",
"pages": "Sida { page } av { totalPages }",
"prev_page": "Föregående sida",
"query_id": "Fråga efter tillgångs-ID-nummer: { id }",
"reset_search": "Återställ sökning",
"tip_1": "Platser och etiketter filter använder 'OR' funktionen. Om fler än en är valda, endast en kommer\nkrävas för en träff.",
"tip_2": "Sökningar med prefixet '#'' kommer att fråga efter ett tillgångs-ID (exempel '#000-001')",
"tip_3": "Fältfilter använder 'OR' funktion. Om fler än en är valda endast en kommer att bli krävande för en\nträff.",
"tips": "Tips",
"tips_sub": "Sök Tips",
"updated_at": "Uppdaterad",
"results": "{ total } Resultat"
}
}

128
frontend/locales/tr.json Normal file
View File

@@ -0,0 +1,128 @@
{
"global": {
"version": "Versiyon:{ version }",
"password": "Şifre",
"create": "Oluştur",
"github": "GitHub projesi",
"join_discord": "Discord'a Katılın",
"follow_dev": "Geliştiriciyi takip edin",
"read_docs": "Dokümanları okuyun",
"email": "Elektronik posta",
"submit": "Gönder",
"confirm": "Onaylayın",
"build": "Sürüm: { build }",
"create_and_add": "Oluştur ve Bir Tane Daha Ekle",
"created": "Oluşturuldu",
"items": "Öğeler",
"labels": "Etiketler",
"locations": "Konumlar",
"name": "İsim",
"search": "Ara",
"sign_out": "Oturumu kapat",
"welcome": "Hoşgeldib, { username }"
},
"components": {
"app": {
"import_dialog": {
"change_warning": "Mevcut import_refs ile içe aktarmaların davranışı değişti. CSV dosyasında bir import_ref varsa, \nöğe CSV dosyasındaki değerlerle güncellenecektir.",
"title": "CSV dosyasını içeri aktar",
"upload": "Yükle",
"description": "Öğelerinizi, etiketlerinizi ve konumlarınızı içeren bir CSV dosyasını içe aktarın. Daha fazla\nbilgi için dökümanları okuyun."
}
},
"global": {
"password_score": {
"password_strength": "Şifre güvenlik seviyesi"
},
"page_qr_code": {
"page_url": "Sayfa URL'si"
}
},
"item": {
"create_modal": {
"title": "Eşya Oluştur",
"photo_button": "Fotoğraf 📷"
},
"view": {
"selectable": {
"items": "Öğeler",
"card": "Kart",
"table": "Tablo",
"no_items": "Görüntülecek Öge Yok"
}
}
},
"label": {
"create_modal": {
"title": "Etiket oluştur"
}
},
"location": {
"create_modal": {
"title": "Konum oluştur"
}
}
},
"index": {
"remember_me": "Beni Hatırla",
"tagline": "Eşyalarınızı Takip Edin, Düzenleyin ve Yönetin.",
"disabled_registration": "Kayıt olma devre dışı",
"login": "Oturum Aç",
"register": "Kaydolun",
"set_email": "E-posta adresiniz nedir?",
"set_password": "Şifrenizi belirleyin",
"set_name": "Adın ne?",
"joining_group": "Mevcut bir gruba katılıyorsunuz!",
"dont_join_group": "Bir gruba katılmak istemiyor musunuz?"
},
"items": {
"add": "Ekle",
"created_at": "Oluşturulma",
"custom_fields": "Özel Alanlar",
"field_selector": "alan seçici",
"field_value": "Alan Değeri",
"first": "Birinci",
"include_archive": "Arşivlenen Öğeleri Dahil Et",
"last": "Son",
"negate_labels": "Seçili Etiketleri Yoksay",
"next_page": "Sonraki Sayfa",
"no_results": "Öğe Bulunamadı",
"options": "Seçenekler",
"order_by": "Sıralama ölçütü",
"pages": "Sayfa { page }/{ totalPages }",
"prev_page": "Önceki Sayfa",
"query_id": "Varlık Kimlik Numarası Sorgulanıyor: { id }",
"tip_1": "Konum ve etiket filtreleri 'veya' işlemini kullanır. Eğer birden fazla seçilirse sadece biri \neşleştirme için kullanılacaktır.",
"reset_search": "Aramayı Sıfırla",
"tips": "İpuçları",
"results": "{ total } Sonuç",
"tip_2": "'#' ile başlayan aramalar bir varlık kimliğini sorgular (örneğin '#000-001')",
"tip_3": "Alan filtreleri 'VEYA' işlemini kullanır. Birden fazla seçenek seçilirse, eşleşme için yalnızca birinin \nkarşılanması yeterlidir.",
"tips_sub": "Arama İpuçları",
"updated_at": "Güncellendiği Zaman"
},
"profile": {
"url": "URL",
"user_profile": "Kullanıcı Profili",
"user_profile_sub": "Kullanıcıları davet edin ve hesabınızı yönetin.",
"active": "Aktif",
"change_password": "Şifre Değiştir",
"currency_format": "Para Birimi Biçimi",
"current_password": "Mevcut Şifre",
"delete_account": "Hesabı Sil",
"delete_account_sub": "Hesabınızı ve ona bağlı tüm verileri silin. Bu işlem geri alınamaz.",
"enabled": "Etkinleştirildi",
"gen_invite": "Davet Bağlantısı Oluştur",
"group_settings": "Grup Ayarları",
"group_settings_sub": "Paylaşılan Grup Ayarları. Bazı ayarların uygulanabilmesi için tarayıcınızı yenilemeniz gerekebilir.",
"inactive": "Etkin Değil",
"new_password": "Yeni Şifre",
"notifier_modal": "{ type, select, true {Edit} false {Create} other {Other}} Bildirici",
"notifiers": "Bildirimde Bulunanlar",
"test": "Test",
"theme_settings": "Tema Ayarları",
"theme_settings_sub": "Tema ayarları tarayıcınızın yerel depolama alanında saklanır. Temayı istediğiniz zaman değiştirebilirsiniz. \nTemanızı ayarlamakta sorun yaşıyorsanız, tarayıcınızı yenilemeyi deneyin.",
"update_group": "Grubu Güncelle",
"notifiers_sub": "Yaklaşan bakım hatırlatmaları için bildirimler alın"
}
}

View File

@@ -0,0 +1,128 @@
{
"components": {
"app": {
"import_dialog": {
"title": "导入 CSV 文件",
"upload": "上传",
"description": "导入包含物品、标签和位置的CSV文件。更多有关信息\n请参阅文档查看所需格式。",
"change_warning": "导入行为会导致现有的 import_refs 的字段被覆盖。\n如果 CSV 文件中存在 import_ref则将使用 CSV 文件中的值更新该项。"
}
},
"item": {
"create_modal": {
"title": "创建物品",
"photo_button": "照片 📷"
},
"view": {
"selectable": {
"card": "卡片模式",
"items": "物品",
"no_items": "没有物品可用展示",
"table": "表格模式"
}
}
},
"location": {
"create_modal": {
"title": "创建位置"
}
},
"global": {
"page_qr_code": {
"page_url": "页面URL"
},
"password_score": {
"password_strength": "密码强度"
}
},
"label": {
"create_modal": {
"title": "创建标签"
}
}
},
"global": {
"create_and_add": "保存并继续创建",
"email": "邮箱",
"items": "物品",
"build": "编译:{build}",
"confirm": "确认",
"create": "创建",
"created": "已创建",
"follow_dev": "关注开发者",
"github": "Github项目",
"join_discord": "加入Discord讨论",
"labels": "标签",
"sign_out": "注销",
"version": "版本:{version}",
"welcome": "欢迎,{username}",
"name": "名称",
"password": "密码",
"read_docs": "查阅文档",
"search": "搜索",
"submit": "提交",
"locations": "位置"
},
"index": {
"disabled_registration": "已禁用注册",
"dont_join_group": "不想加入群组?",
"joining_group": "您正在加入现有群组!",
"login": "登录",
"register": "注册",
"set_name": "你的名称叫什么?",
"remember_me": "记住我",
"set_email": "您的电子邮箱是什么?",
"tagline": "跟踪、整理和管理您的物品。",
"set_password": "设置密码"
},
"items": {
"last": "最后一页",
"negate_labels": "取消选中的标签",
"next_page": "下一页",
"no_results": "没有可显示的物品",
"options": "选项",
"prev_page": "上一页",
"add": "添加",
"field_selector": "字段选择",
"first": "第一页",
"order_by": "排序方式",
"pages": "第{page}页,共{totalPages}页",
"query_id": "查询资产ID {id}",
"created_at": "创建于",
"custom_fields": "自定义字段",
"field_value": "字段值",
"include_archive": "包括已存档的项目",
"reset_search": "重置搜索",
"results": "{ total } 条结果",
"tip_1": "位置和标签过滤器使用“或”操作。如果选择了多个位置或标签,\n则只需要任意一个匹配上即可。",
"tip_2": "以“#”为前缀的搜索将变成查询资产ID例如“#000-001” ",
"tip_3": "字段过滤器使用“或”操作。如果选择了多个位置或标签,\n则只需要任意一个匹配上即可。",
"tips": "建议",
"tips_sub": "搜索提示",
"updated_at": "更新于"
},
"profile": {
"group_settings_sub": "共享组设置。您可能需要刷新浏览器来让某些设置生效。",
"notifier_modal": "{ type, select, true {编辑} false {创建} other {Other}} 通知器",
"active": "活跃",
"delete_account_sub": "删除您的帐户及其所有相关数据。这是无法撤消的。",
"inactive": "非活跃",
"theme_settings_sub": "主题设置存储在浏览器的本地存储中。您可以随时更改主题。\n如果您在设置主题时遇到问题请尝试刷新浏览器。",
"update_group": "更新组",
"change_password": "更改密码",
"currency_format": "货币格式",
"current_password": "原密码",
"delete_account": "删除帐户",
"enabled": "已启用",
"gen_invite": "生成邀请链接",
"group_settings": "组设置",
"new_password": "新密码",
"notifiers": "通知器",
"notifiers_sub": "获取即将到来的维护提醒通知",
"test": "测试",
"theme_settings": "主题设置",
"url": "网址",
"user_profile": "用户资料",
"user_profile_sub": "邀请用户共同管理您的资产。"
}
}

View File

@@ -0,0 +1,128 @@
{
"items": {
"include_archive": "包括存檔項目",
"tip_1": "位置和標籤過濾器使用“OR”運算。如果選擇了多個則一次配對\n只會使用一個。",
"pages": "第{ page } 頁(共{ totalPages })",
"tip_3": "字段過濾器使用“OR”運算。如果選擇了多個則一次配對只需要選擇\n一個。",
"field_value": "欄位值",
"field_selector": "欄位選擇器",
"custom_fields": "自訂欄位",
"first": "第一項",
"add": "添加",
"created_at": "創建於",
"last": "最後一項",
"negate_labels": "取消選定的標籤",
"next_page": "下一頁",
"no_results": "沒有找到項目",
"options": "選項",
"order_by": "排序",
"prev_page": "上一頁",
"query_id": "查詢資產ID號碼:{ id }",
"reset_search": "重置搜尋",
"results": "{ total }結果",
"tips": "提示",
"tips_sub": "搜尋技巧",
"updated_at": "更新於",
"tip_2": "以「#」為前綴的搜尋將查詢資產 ID例如「#000-001」"
},
"components": {
"app": {
"import_dialog": {
"description": "匯入包含您的商品、標籤和位置的 CSV 檔案。有關所需格式的更多信息\n請參閱文件。",
"title": "匯入CSV檔案",
"upload": "上傳",
"change_warning": "現有 import_refs 的導入行為已變更。如果 CSV 檔案中存在 import_ref則\n項目將使用 CSV 檔案中的值進行更新。"
}
},
"global": {
"page_qr_code": {
"page_url": "頁面網址"
},
"password_score": {
"password_strength": "密碼強度"
}
},
"item": {
"create_modal": {
"title": "新增項目",
"photo_button": "相片 📷"
},
"view": {
"selectable": {
"card": "卡牌風",
"items": "項目",
"no_items": "沒有可顯示的項目",
"table": "表格"
}
}
},
"label": {
"create_modal": {
"title": "新增標籤"
}
},
"location": {
"create_modal": {
"title": "新增地點"
}
}
},
"global": {
"build": "建立{ build }",
"confirm": "確認",
"create": "新增",
"create_and_add": "創建並添加另一個",
"created": "已建立",
"email": "電子信箱",
"github": "GitHub項目",
"items": "項目",
"join_discord": "加入Discord",
"labels": "標籤",
"locations": "地點",
"name": "名字",
"password": "密碼",
"read_docs": "閱讀文件",
"search": "搜尋",
"sign_out": "登出",
"submit": "提交",
"version": "版本:{ version }",
"follow_dev": "關注開發者",
"welcome": "歡迎,{ username }"
},
"index": {
"disabled_registration": "已禁用註冊",
"dont_join_group": "不想加入群組?",
"joining_group": "您正在加入現有群組!",
"login": "登入",
"register": "註冊",
"remember_me": "記住帳號",
"set_email": "你的電子郵件是什麼?",
"set_name": "你叫什麼名字?",
"tagline": "追蹤、組織和管理您的物品。",
"set_password": "設定您的密碼"
},
"profile": {
"change_password": "變更密碼",
"currency_format": "貨幣格式",
"current_password": "當前密碼",
"delete_account": "刪除帳戶",
"enabled": "啟用",
"gen_invite": "產生邀請連結",
"group_settings": "群組設定",
"group_settings_sub": "共享群組設定。您可能需要刷新瀏覽器才能套用設定。",
"inactive": "不活躍",
"new_password": "新密碼",
"active": "活躍",
"delete_account_sub": "刪除您的帳戶及其所有相關資料。此操作無法撤回。",
"notifiers": "通知者",
"notifiers_sub": "獲取即將到來的維護提醒的通知",
"test": "測試",
"theme_settings": "主題設定",
"update_group": "更新群組",
"url": "網址",
"user_profile": "使用者資料",
"user_profile_sub": "邀請使用者並管理您的帳戶。",
"theme_settings_sub": "主題設定儲存在瀏覽器的本機儲存中。您可以隨時變更主題。如果你是\n 設定主題時遇到問題,請嘗試刷新瀏覽器。",
"notifier_modal": "通知 { type, select, true {Edit} false {Create} other {Other}}"
}
}

View File

@@ -3,6 +3,9 @@ import { defineNuxtConfig } from "nuxt/config";
// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
ssr: false,
build: {
transpile: ["vue-i18n"],
},
modules: [
"@nuxtjs/tailwindcss",
"@pinia/nuxt",

View File

@@ -16,6 +16,7 @@
"devDependencies": {
"@faker-js/faker": "^8.0.0",
"@iconify-json/mdi": "^1.1.64",
"@intlify/unplugin-vue-i18n": "^4.0.0",
"@nuxtjs/eslint-config-typescript": "^12.0.0",
"@types/dompurify": "^3.0.0",
"@types/markdown-it": "^13.0.0",
@@ -27,13 +28,15 @@
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-vue": "^9.4.0",
"h3": "^1.7.1",
"intl-messageformat": "^10.5.14",
"isomorphic-fetch": "^3.0.0",
"nuxt": "3.6.5",
"prettier": "^3.2.5",
"typescript": "^5.0.0",
"unplugin-icons": "^0.18.5",
"vite-plugin-eslint": "^1.8.1",
"vitest": "^1.0.0"
"vitest": "^1.0.0",
"vue-i18n": "^9.13.1"
},
"dependencies": {
"@headlessui/vue": "^1.7.9",

View File

@@ -1,6 +1,5 @@
<script setup lang="ts">
import MdiGithub from "~icons/mdi/github";
import MdiTwitter from "~icons/mdi/twitter";
import MdiDiscord from "~icons/mdi/discord";
import MdiFolder from "~icons/mdi/folder";
import MdiAccount from "~icons/mdi/account";
@@ -8,6 +7,7 @@
import MdiLogin from "~icons/mdi/login";
import MdiArrowRight from "~icons/mdi/arrow-right";
import MdiLock from "~icons/mdi/lock";
import MdiMastodon from '~icons/mdi/mastodon';
useHead({
title: "Homebox | Organize and Tag Your Stuff",
@@ -77,6 +77,15 @@
async function registerUser() {
loading.value = true;
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email.value)) {
toast.error("Invalid email address");
loading.value = false;
return;
}
const { error } = await api.register({
name: username.value,
email: email.value,
@@ -149,19 +158,19 @@
<AppLogo class="w-12 -mb-4" />
x
</h2>
<p class="ml-1 text-lg text-base-content/50">Track, Organize, and Manage your Things.</p>
<p class="ml-1 text-lg text-base-content/50">{{ $t("index.tagline") }}</p>
</div>
<div class="flex mt-6 sm:mt-0 gap-4 ml-auto text-neutral-content">
<a class="tooltip" data-tip="Project Github" href="https://github.com/sysadminsmedia/homebox" target="_blank">
<a class="tooltip" :data-tip="$t('global.github')" href="https://github.com/sysadminsmedia/homebox" target="_blank">
<MdiGithub class="h-8 w-8" />
</a>
<a href="https://twitter.com/haybytes" class="tooltip" data-tip="Follow The Developer" target="_blank">
<MdiTwitter class="h-8 w-8" />
<a href="https://noc.social/@sysadminsmedia" class="tooltip" :data-tip="$t('global.follow_dev')" target="_blank">
<MdiMastodon class="h-8 w-8" />
</a>
<a href="https://discord.gg/aY4DCkpNA9" class="tooltip" data-tip="Join The Discord" target="_blank">
<a href="https://discord.gg/aY4DCkpNA9" class="tooltip" :data-tip="$t('global.join_discord')" target="_blank">
<MdiDiscord class="h-8 w-8" />
</a>
<a href="https://homebox.sysadminsmedia.com/en/" class="tooltip" data-tip="Read The Docs" target="_blank">
<a href="https://homebox.sysadminsmedia.com/en/" class="tooltip" :data-tip="$t('global.read_docs')" target="_blank">
<MdiFolder class="h-8 w-8" />
</a>
</div>
@@ -174,17 +183,17 @@
<div class="card-body">
<h2 class="card-title text-2xl align-center">
<MdiAccount class="mr-1 w-7 h-7" />
Register
{{ $t("index.register") }}
</h2>
<FormTextField v-model="email" label="Set your email?" />
<FormTextField v-model="username" label="What's your name?" />
<FormTextField v-model="email" :label="$t('index.set_email')" />
<FormTextField v-model="username" :label="$t('index.set_name')" />
<div v-if="!(groupToken == '')" class="pt-4 pb-1 text-center">
<p>You're Joining an Existing Group!</p>
<p>{{ $t("index.joining_group") }}</p>
<button type="button" class="text-xs underline" @click="groupToken = ''">
Don't Want To Join a Group?
{{ $t("index.dont_join_group") }}
</button>
</div>
<FormPassword v-model="password" label="Set your password" />
<FormPassword v-model="password" :label="$t('index.set_password')" />
<PasswordScore v-model:valid="canRegister" :password="password" />
<div class="card-actions justify-end">
<button
@@ -193,7 +202,7 @@
:class="loading ? 'loading' : ''"
:disabled="loading || !canRegister"
>
Register
{{ $t("index.register") }}
</button>
</div>
</div>
@@ -204,17 +213,17 @@
<div class="card-body">
<h2 class="card-title text-2xl align-center">
<MdiAccount class="mr-1 w-7 h-7" />
Login
{{ $t("index.login") }}
</h2>
<template v-if="status && status.demo">
<p class="text-xs italic text-center">This is a demo instance</p>
<p class="text-xs text-center"><b>Email</b> demo@example.com</p>
<p class="text-xs text-center"><b>Password</b> demo</p>
<p class="text-xs text-center"><b>{{ $t("global.email") }}</b> demo@example.com</p>
<p class="text-xs text-center"><b>{{ $t("global.password") }}</b> demo</p>
</template>
<FormTextField v-model="email" label="Email" />
<FormPassword v-model="loginPassword" label="Password" />
<FormTextField v-model="email" :label="$t('global.email')" />
<FormPassword v-model="loginPassword" :label="$t('global.password')" />
<div class="max-w-[140px]">
<FormCheckbox v-model="remember" label="Remember Me" />
<FormCheckbox v-model="remember" :label="$t('index.remember_me')" />
</div>
<div class="card-actions justify-end">
<button
@@ -223,7 +232,7 @@
:class="loading ? 'loading' : ''"
:disabled="loading"
>
Login
{{ $t("index.login") }}
</button>
</div>
</div>
@@ -241,18 +250,21 @@
<MdiLogin v-else class="w-5 h-5 swap-off" />
<MdiArrowRight class="w-5 h-5 swap-on" />
</template>
{{ registerForm ? "Login" : "Register" }}
{{ registerForm ? $t("index.login") : $t("index.register") }}
</BaseButton>
<p v-else class="text-base-content italic text-sm inline-flex items-center gap-2">
<MdiLock class="w-4 h-4 inline-block" />
Registration Disabled
{{ $t("disabled_registration") }}
</p>
</div>
</div>
</div>
</div>
<footer v-if="status" class="mt-auto text-center w-full bottom-0 pb-4">
<p class="text-center text-sm">Version: {{ status.build.version }} ~ Build: {{ status.build.commit }}</p>
<p class="text-center text-sm">
{{ $t("global.version", { version: status.build.version }) }} ~
{{ $t("global.build", { build: status.build.commit }) }}
</p>
</footer>
</div>
</template>

View File

@@ -40,6 +40,7 @@
const includeArchived = useRouteQuery("archived", false);
const fieldSelector = useRouteQuery("fieldSelector", false);
const negateLabels = useRouteQuery("negateLabels", false);
const orderBy = useRouteQuery("orderBy", "name");
const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
const hasNext = computed(() => page.value * pageSize.value < total.value);
@@ -169,6 +170,12 @@
}
});
watch(orderBy, (newV, oldV) => {
if (newV !== oldV) {
search();
}
});
async function fetchValues(field: string): Promise<string[]> {
if (fieldValuesCache.value[field]) {
return fieldValuesCache.value[field];
@@ -201,6 +208,7 @@
pageSize: pageSize.value,
includeArchived: includeArchived.value ? "true" : "false",
negateLabels: negateLabels.value ? "true" : "false",
orderBy: orderBy.value,
},
});
}
@@ -231,6 +239,7 @@
includeArchived: includeArchived.value,
page: page.value,
pageSize: pageSize.value,
orderBy: orderBy.value,
fields,
});
@@ -278,6 +287,7 @@
archived: includeArchived.value ? "true" : "false",
fieldSelector: fieldSelector.value ? "true" : "false",
negateLabels: negateLabels.value ? "true" : "false",
orderBy: orderBy.value,
pageSize: pageSize.value,
page: page.value,
q: query.value,
@@ -311,6 +321,7 @@
fieldSelector: "false",
pageSize: 10,
page: 1,
orderBy: "name",
q: "",
loc: [],
lab: [],
@@ -327,9 +338,9 @@
<div v-if="locations && labels">
<div class="flex flex-wrap md:flex-nowrap gap-4 items-end">
<div class="w-full">
<FormTextField v-model="query" placeholder="Search" />
<FormTextField v-model="query" :placeholder="$t('global.search')" />
<div v-if="byAssetId" class="text-sm pl-2 pt-2">
<p>Querying Asset ID Number: {{ parsedAssetId }}</p>
<p>{{ $t("items.query_id", { id: parsedAssetId }) }}</p>
</div>
</div>
<BaseButton class="btn-block md:w-auto" @click.prevent="submit">
@@ -337,12 +348,12 @@
<MdiLoading v-if="loading" class="animate-spin" />
<MdiMagnify v-else />
</template>
Search
{{ $t("global.search") }}
</BaseButton>
</div>
<div class="flex flex-wrap md:flex-nowrap gap-2 w-full py-2">
<SearchFilter v-model="selectedLocations" label="Locations" :options="locationFlatTree">
<SearchFilter v-model="selectedLocations" :label="$t('global.locations')" :options="locationFlatTree">
<template #display="{ item }">
<div>
<div class="flex w-full">
@@ -354,52 +365,60 @@
</div>
</template>
</SearchFilter>
<SearchFilter v-model="selectedLabels" label="Labels" :options="labels" />
<SearchFilter v-model="selectedLabels" :label="$t('global.labels')" :options="labels" />
<div class="dropdown">
<label tabindex="0" class="btn btn-xs">Options</label>
<label tabindex="0" class="btn btn-xs">{{ $t("items.options") }}</label>
<div
tabindex="0"
class="dropdown-content mt-1 max-h-72 p-4 w-64 overflow-auto shadow bg-base-100 rounded-md -translate-x-24"
>
<label class="label cursor-pointer mr-auto">
<input v-model="includeArchived" type="checkbox" class="toggle toggle-sm toggle-primary" />
<span class="label-text ml-4"> Include Archived Items </span>
<span class="label-text ml-4"> {{ $t("items.include_archive") }} </span>
</label>
<label class="label cursor-pointer mr-auto">
<input v-model="fieldSelector" type="checkbox" class="toggle toggle-sm toggle-primary" />
<span class="label-text ml-4"> Field Selector </span>
<span class="label-text ml-4"> {{ $t("items.field_selector") }} </span>
</label>
<label class="label cursor-pointer mr-auto">
<input v-model="negateLabels" type="checkbox" class="toggle toggle-sm toggle-primary" />
<span class="label-text ml-4"> Negate selected labels </span>
<span class="label-text ml-4"> {{ $t("items.negate_labels") }} </span>
</label>
<label class="label cursor-pointer mr-auto">
<select v-model="orderBy" class="select select-bordered select-sm">
<option value="name" selected>{{ $t("global.name") }}</option>
<option value="createdAt">{{ $t("items.created_at") }}</option>
<option value="updatedAt">{{ $t("items.updated_at") }}</option>
</select>
<span class="label-text ml-4"> {{ $t("items.order_by") }} </span>
</label>
<hr class="my-2" />
<BaseButton class="btn-block btn-sm" @click="reset"> Reset Search</BaseButton>
<BaseButton class="btn-block btn-sm" @click="reset"> {{ $t("items.reset_search") }} </BaseButton>
</div>
</div>
<div class="dropdown ml-auto dropdown-end">
<label tabindex="0" class="btn btn-xs">Tips</label>
<label tabindex="0" class="btn btn-xs">{{ $t("items.tips") }}</label>
<div
tabindex="0"
class="dropdown-content mt-1 p-4 w-[325px] text-sm overflow-auto shadow bg-base-100 rounded-md"
>
<p class="text-base">Search Tips</p>
<p class="text-base">{{ $t("items.tips_sub") }}</p>
<ul class="mt-1 list-disc pl-6">
<li>
Location and label filters use the 'OR' operation. If more than one is selected only one will be
required for a match.
{{ $t("items.tip_1") }}
</li>
<li>Searches prefixed with '#'' will query for a asset ID (example '#000-001')</li>
<li>
Field filters use the 'OR' operation. If more than one is selected only one will be required for a
match.
{{ $t("items.tip_2") }}
</li>
<li>
{{ $t("items.tip_3") }}
</li>
</ul>
</div>
</div>
</div>
<div v-if="fieldSelector" class="py-4 space-y-2">
<p>Custom Fields</p>
<p>{{ $t("items.custom_fields") }}</p>
<div v-for="(f, idx) in fieldTuples" :key="idx" class="flex flex-wrap gap-2">
<div class="form-control w-full max-w-xs">
<label class="label">
@@ -416,7 +435,7 @@
</div>
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Field Value</span>
<span class="label-text">{{ $t("items.field_value") }}</span>
</label>
<select v-model="fieldTuples[idx][1]" class="select-bordered select" :items="fieldValuesCache[f[0]]">
<option v-for="v in fieldValuesCache[f[0]]" :key="v" :value="v">{{ v }}</option>
@@ -430,38 +449,42 @@
<MdiDelete class="w-5 h-5" />
</button>
</div>
<BaseButton type="button" class="btn-sm mt-2" @click="() => fieldTuples.push(['', ''])"> Add</BaseButton>
<BaseButton type="button" class="btn-sm mt-2" @click="() => fieldTuples.push(['', ''])">
{{ $t("items.add") }}
</BaseButton>
</div>
</div>
<section class="mt-10">
<BaseSectionHeader ref="itemsTitle"> Items </BaseSectionHeader>
<BaseSectionHeader ref="itemsTitle"> {{ $t("global.items") }} </BaseSectionHeader>
<p class="text-base font-medium flex items-center">
{{ total }} Results
<span class="text-base ml-auto"> Page {{ page }} of {{ totalPages }}</span>
{{ $t("items.results", { total: total }) }}
<span class="text-base ml-auto"> {{ $t("items.pages", { page: page, totalPages: totalPages }) }} </span>
</p>
<div ref="cardgrid" class="grid mt-4 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
<ItemCard v-for="item in items" :key="item.id" :item="item" />
<div class="hidden first:inline text-xl">No Items Found</div>
<div class="hidden first:inline text-xl">{{ $t("items.no_results") }}</div>
</div>
<div v-if="items.length > 0 && (hasNext || hasPrev)" class="mt-10 flex gap-2 flex-col items-center">
<div class="flex">
<div class="btn-group">
<button :disabled="!hasPrev" class="btn text-no-transform" @click="prev">
<MdiChevronLeft class="mr-1 h-6 w-6" name="mdi-chevron-left" />
Prev
{{ $t("items.prev_page") }}
</button>
<button v-if="hasPrev" class="btn text-no-transform" @click="page = 1">{{ $t("items.first") }}</button>
<button v-if="hasNext" class="btn text-no-transform" @click="page = totalPages">
{{ $t("items.last") }}
</button>
<button v-if="hasPrev" class="btn text-no-transform" @click="page = 1">First</button>
<button v-if="hasNext" class="btn text-no-transform" @click="page = totalPages">Last</button>
<button :disabled="!hasNext" class="btn text-no-transform" @click="next">
Next
{{ $t("items.next_page") }}
<MdiChevronRight class="ml-1 h-6 w-6" name="mdi-chevron-right" />
</button>
</div>
</div>
<p class="text-sm font-bold">Page {{ page }} of {{ totalPages }}</p>
<p class="text-sm font-bold">{{ $t("items.pages", { page: page, totalPages: totalPages }) }}</p>
</div>
</section>
</BaseContainer>

View File

@@ -89,13 +89,6 @@
notify.success("Group updated");
}
const pubApi = usePublicApi();
const { data: status } = useAsyncData(async () => {
const { data } = await pubApi.status();
return data;
});
const { setTheme } = useTheme();
const auth = useAuthContext();
@@ -305,11 +298,11 @@
<template>
<div>
<BaseModal v-model="passwordChange.dialog">
<template #title> Change Password </template>
<template #title> {{ $t("profile.change_password") }} </template>
<form @submit.prevent="changePassword">
<FormPassword v-model="passwordChange.current" label="Current Password" placeholder="" />
<FormPassword v-model="passwordChange.new" label="New Password" placeholder="" />
<FormPassword v-model="passwordChange.current" :label="$t('profile.current_password')" placeholder="" />
<FormPassword v-model="passwordChange.new" :label="$t('profile.new_password')" placeholder="" />
<PasswordScore v-model:valid="passwordChange.isValid" :password="passwordChange.new" />
<div class="flex">
@@ -319,26 +312,26 @@
:disabled="!passwordChange.isValid"
type="submit"
>
Submit
{{ $t("global.submit") }}
</BaseButton>
</div>
</form>
</BaseModal>
<BaseModal v-model="notifierDialog">
<template #title> {{ notifier ? "Edit" : "Create" }} Notifier </template>
<template #title> {{ $t("profile.notifier_modal", {type: (notifier != null)}) }} </template>
<form @submit.prevent="createNotifier">
<template v-if="notifier">
<FormTextField v-model="notifier.name" label="Name" />
<FormTextField v-model="notifier.url" label="URL" />
<FormTextField v-model="notifier.name" :label="$t('global.name')" />
<FormTextField v-model="notifier.url" :label="$t('profile.url')" />
<div class="max-w-[100px]">
<FormCheckbox v-model="notifier.isActive" label="Enabled" />
<FormCheckbox v-model="notifier.isActive" :label="$t('profile.enabled')" />
</div>
</template>
<div class="flex gap-2 justify-between mt-4">
<BaseButton :disabled="!(notifier && notifier.url)" type="button" @click="testNotifier"> Test </BaseButton>
<BaseButton type="submit"> Submit </BaseButton>
<BaseButton :disabled="!(notifier && notifier.url)" type="button" @click="testNotifier"> {{ $t("profile.test") }} </BaseButton>
<BaseButton type="submit"> {{ $t("global.submit") }} </BaseButton>
</div>
</form>
</BaseModal>
@@ -348,8 +341,8 @@
<template #title>
<BaseSectionHeader>
<MdiAccount class="mr-2 -mt-1 text-base-600" />
<span class="text-base-600"> User Profile </span>
<template #description> Invite users, and manage your account. </template>
<span class="text-base-600"> {{ $t("profile.user_profile") }} </span>
<template #description> {{ $t("profile.user_profile_sub") }} </template>
</BaseSectionHeader>
</template>
@@ -357,8 +350,8 @@
<div class="p-4">
<div class="flex gap-2">
<BaseButton size="sm" @click="openPassChange"> Change Password </BaseButton>
<BaseButton size="sm" @click="generateToken"> Generate Invite Link </BaseButton>
<BaseButton size="sm" @click="openPassChange"> {{ $t("profile.change_password") }} </BaseButton>
<BaseButton size="sm" @click="generateToken"> {{ $t("profile.gen_invite") }} </BaseButton>
</div>
<div v-if="token" class="pt-4 flex items-center pl-1">
<CopyText class="mr-2 btn-primary btn btn-outline btn-square btn-sm" :text="tokenUrl" />
@@ -375,8 +368,8 @@
<template #title>
<BaseSectionHeader>
<MdiMegaphone class="mr-2 -mt-1 text-base-600" />
<span class="text-base-600"> Notifiers </span>
<template #description> Get notifications for up coming maintenance reminders </template>
<span class="text-base-600"> {{ $t("profile.notifiers") }} </span>
<template #description> {{ $t("profile.notifiers_sub") }} </template>
</BaseSectionHeader>
</template>
@@ -399,11 +392,11 @@
</div>
<div class="flex justify-between py-1 flex-wrap text-sm">
<p>
<span v-if="n.isActive" class="badge badge-success"> Active </span>
<span v-else class="badge badge-error"> Inactive</span>
<span v-if="n.isActive" class="badge badge-success"> {{ $t("profile.active") }} </span>
<span v-else class="badge badge-error"> {{ $t("profile.inactive") }} </span>
</p>
<p>
Created
{{ $t("global.created") }}
<DateTime format="relative" datetime-type="time" :date="n.createdAt" />
</p>
</div>
@@ -411,7 +404,7 @@
</div>
<div class="p-4">
<BaseButton size="sm" @click="openNotifierDialog"> Create </BaseButton>
<BaseButton size="sm" @click="openNotifierDialog"> {{ $t("global.create") }} </BaseButton>
</div>
</BaseCard>
@@ -419,19 +412,19 @@
<template #title>
<BaseSectionHeader class="pb-0">
<MdiAccountMultiple class="mr-2 -mt-1 text-base-600" />
<span class="text-base-600"> Group Settings </span>
<span class="text-base-600"> {{ $t("profile.group_settings") }} </span>
<template #description>
Shared Group Settings. You may need to refresh your browser for some settings to apply.
{{ $t("profile.group_settings_sub") }}
</template>
</BaseSectionHeader>
</template>
<div v-if="group && currencies && currencies.length > 0" class="p-5 pt-0">
<FormSelect v-model="currency" label="Currency Format" :items="currencies" />
<FormSelect v-model="currency" :label="$t('profile.currency_format')" :items="currencies" />
<p class="m-2 text-sm">Example: {{ currencyExample }}</p>
<div class="mt-4">
<BaseButton size="sm" @click="updateGroup"> Update Group </BaseButton>
<BaseButton size="sm" @click="updateGroup"> {{ $t("profile.update_group") }} </BaseButton>
</div>
</div>
</BaseCard>
@@ -440,10 +433,9 @@
<template #title>
<BaseSectionHeader>
<MdiFill class="mr-2 text-base-600" />
<span class="text-base-600"> Theme Settings </span>
<span class="text-base-600"> {{ $t("profile.theme_settings") }} </span>
<template #description>
Theme settings are stored in your browser's local storage. You can change the theme at any time. If you're
having trouble setting your theme try refreshing your browser.
{{ $t("profile.theme_settings_sub") }}
</template>
</BaseSectionHeader>
</template>
@@ -491,18 +483,15 @@
<template #title>
<BaseSectionHeader>
<MdiDelete class="mr-2 -mt-1 text-base-600" />
<span class="text-base-600"> Delete Account</span>
<template #description> Delete your account and all its associated data. </template>
<span class="text-base-600"> {{ $t("profile.delete_account") }} </span>
<template #description> {{ $t("profile.delete_account_sub") }} </template>
</BaseSectionHeader>
</template>
<div class="p-4 px-6 border-t-2 border-gray-300">
<BaseButton size="sm" class="btn-error" @click="deleteProfile"> Delete Account </BaseButton>
<BaseButton size="sm" class="btn-error" @click="deleteProfile"> {{ $t("profile.delete_account") }} </BaseButton>
</div>
</BaseCard>
</BaseContainer>
<footer v-if="status" class="text-center w-full bottom-0 pb-4">
<p class="text-center text-sm">Version: {{ status.build.version }} ~ Build: {{ status.build.commit }}</p>
</footer>
</div>
</template>

View File

@@ -308,7 +308,7 @@
</p>
<p>
This feature is in early development stages and may change in future releases, if you have feedback please
provide it in the <a href="https://github.com/sysadminsmedia/homebox/discussions/273">GitHub Discussion</a>
provide it in the <a href="https://github.com/sysadminsmedia/homebox/discussions/53">GitHub Discussion</a>
</p>
<h2>Tips</h2>
<ul>

View File

@@ -42,8 +42,9 @@
<div class="border-t px-6 pb-3 border-gray-300 divide-gray-300 divide-y">
<DetailAction @action="modals.import = true">
<template #title>Import Inventory</template>
Imports the standard CSV format for Homebox. This will <b>not</b> overwrite any existing items in your
inventory. It will only add new items.
Imports the standard CSV format for Homebox. Without an <code>HB.import_ref</code> column, this will
<b>not</b> overwrite any existing items in your inventory, only add new items. Rows with an
<code>HB.import_ref</code> column are merged into existing items with the same import_ref, if one exists.
</DetailAction>
<DetailAction @action="getExportCSV()">
<template #title>Export Inventory</template>
@@ -104,7 +105,7 @@
middleware: ["auth"],
});
useHead({
title: "Homebox | Profile",
title: "Homebox | Tools",
});
const modals = ref({

63
frontend/plugins/i18n.ts Normal file
View File

@@ -0,0 +1,63 @@
import type { CompileError, MessageCompiler, MessageContext } from "vue-i18n";
import { createI18n } from "vue-i18n";
import { IntlMessageFormat } from "intl-messageformat";
export default defineNuxtPlugin(({ vueApp }) => {
function checkDefaultLanguage() {
let matched = null;
const languages = Object.getOwnPropertyNames(messages())
languages.forEach(lang => {
if (lang === navigator.language.replace('-', '_')) {
matched = lang;
}
});
if (!matched) {
languages.forEach(lang => {
const languagePartials = navigator.language.split('-')[0]
if (lang === languagePartials) {
matched = lang;
}
});
}
return matched;
}
const i18n = createI18n({
legacy: false,
globalInjection: true,
locale: checkDefaultLanguage() || "en",
fallbackLocale: "en",
messageCompiler,
messages: messages(),
});
vueApp.use(i18n);
});
export const messageCompiler: MessageCompiler = (message, { locale, key, onError }) => {
if (typeof message === "string") {
/**
* You can tune your message compiler performance more with your cache strategy or also memoization at here
*/
const formatter = new IntlMessageFormat(message, locale);
return (ctx: MessageContext) => {
return formatter.format(ctx.values);
};
} else {
/**
* for AST.
* If you would like to support it,
* You need to transform locale messages such as `json`, `yaml`, etc. with the bundle plugin.
*/
onError && onError(new Error("not support for AST") as CompileError);
return () => key;
}
};
export const messages: Object = () => {
let messages = {};
const modules = import.meta.glob('~//locales/**.json', { eager: true });
for (const path in modules) {
const key = path.slice(9, -5);
messages[key] = modules[path];
}
return messages;
};

12656
frontend/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff