mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2026-03-23 02:35:41 +00:00
refactor(webapp): vue 3 migration - ds-form (#9407)
This commit is contained in:
parent
16c4f03d3f
commit
906ac801be
@ -14,7 +14,8 @@ Phase 3: OsButton ██████████ 100% (133/133 Buttons) ✅
|
||||
Phase 4: Tier 1 ██████████ 100% (OsButton, OsIcon, OsSpinner, OsCard) ✅
|
||||
Phase 4: Tier A → HTML ██████████ 100% (10 ds-* Wrapper → Plain HTML) ✅
|
||||
Phase 4: Tier B ██████████ 100% (ds-chip→OsBadge✅, ds-tag→OsBadge✅, ds-grid✅, ds-number→OsNumber✅, ds-radio→HTML✅)
|
||||
Phase 4: Tier B+ ████████░░ 75% (ds-table→HTML✅) | Tier 2 begonnen (OsModal✅) | Rest ausstehend (OsInput, OsMenu, OsSelect)
|
||||
Phase 4: Tier B ██████████ 100% (Chip→OsBadge, Tag→OsBadge, Grid→HTML, Number→OsNumber, Radio→HTML, Table→HTML) ✅
|
||||
Phase 4: Tier 2+ ██████░░░░ 50% (OsModal✅, ds-form entkoppelt✅) | Rest ausstehend (OsInput, OsMenu, OsSelect, OsDropdown, OsAvatar)
|
||||
```
|
||||
|
||||
### Statistiken
|
||||
@ -29,7 +30,7 @@ Phase 4: Tier B+ ████████░░ 75% (ds-table→HTML✅)
|
||||
| ✅ → OsNumber | Number (5 Nutzungen → OsNumber, CountTo.vue gelöscht, vue-count-to entfernt) |
|
||||
| ✅ → Plain HTML | Radio (1 Datei → native `<input type="radio">` in ReportModal) |
|
||||
| ⬜ → UI-Library | Modal, Input, Menu, MenuItem, Select (5) — Tier 2-3 |
|
||||
| ⬜ Offen | Form (18 Dateien — HTML oder OsForm?) |
|
||||
| ✅ ds-form entkoppelt | Form-Validierung → formValidation Mixin (async-validator), ds-input/ds-select bleiben als UI-Komponenten |
|
||||
| ⬜ Nicht in Webapp | Code, CopyField, FormItem, InputError, InputLabel, Page, PageTitle, Logo, Avatar, TableCol, TableHeadCol (11) |
|
||||
|
||||
### OsButton Migration (Phase 3) ✅
|
||||
@ -38,7 +39,7 @@ Phase 4: Tier B+ ████████░░ 75% (ds-table→HTML✅)
|
||||
|
||||
**Erkenntnisse aus der Migration:**
|
||||
- `type="submit"` muss explizit gesetzt werden (OsButton Default: `type="button"`)
|
||||
- DsForm `errors` ist ein Objekt → `!!errors` für Boolean-Cast bei `:disabled`
|
||||
- `formErrors` ist ein Objekt → `!!formErrors` für Boolean-Cast bei `:disabled`
|
||||
- CSS `.base-button` Selektoren → `> button` oder `button`
|
||||
- Filter-Buttons nutzen `:appearance="condition ? 'filled' : 'outline'"` Pattern
|
||||
- Circle-Buttons mit Icon: `<template #icon><os-icon :icon="..." /></template>`
|
||||
@ -438,19 +439,19 @@ Phase 4: Tier B+ ████████░░ 75% (ds-table→HTML✅)
|
||||
14. [x] ds-chip (5 Dateien, 20 Nutzungen) → OsBadge (UI-Library)
|
||||
15. [x] ds-tag (3 Dateien) → OsBadge shape="square" (UI-Library)
|
||||
16. [x] ds-grid / ds-grid-item (10 Dateien) → CSS Grid (Plain HTML)
|
||||
17. [ ] ds-number (5 Dateien) → `<div class="ds-number">`
|
||||
17. [x] ds-number (5 Dateien) → OsNumber (UI-Library) ✅
|
||||
18. [x] ds-radio (1 Datei) → native `<input type="radio">` + `<fieldset>` (ReportModal) ✅
|
||||
|
||||
### Phase 4: Tier 2-4 — UI-Library
|
||||
18. [x] OsModal (h() Render, Focus-Trap, Scroll-Lock, A11y; ConfirmModal + ReportModal nutzen OsModal; DeleteUserModal/DisableModal/ReleaseModal gelöscht) ✅
|
||||
19. [ ] OsInput (23 Dateien, gekoppelt mit ds-form)
|
||||
20. [ ] OsMenu / OsMenuItem (17 Dateien)
|
||||
21. [ ] OsSelect (3 Dateien), OsTable (7 Dateien)
|
||||
22. [ ] ds-form → HTML `<form>` oder OsForm (18 Dateien)
|
||||
19. [x] OsModal (h() Render, Focus-Trap, Scroll-Lock, A11y; ConfirmModal + ReportModal nutzen OsModal; DeleteUserModal/DisableModal/ReleaseModal gelöscht) ✅
|
||||
20. [x] ds-form → formValidation Mixin (async-validator), 18 Dateien migriert, vuelidate entfernt ✅
|
||||
21. [ ] OsInput (23 Dateien)
|
||||
22. [ ] OsMenu / OsMenuItem (17 Dateien)
|
||||
23. [ ] OsSelect (3 Dateien), OsTable (7 Dateien)
|
||||
|
||||
---
|
||||
|
||||
**✅ Phase 0-3 abgeschlossen. Phase 4: Tier 1 + Tier A ✅, Tier B 80% (Chip→OsBadge, Tag→OsBadge, Grid→HTML, Number→OsNumber, Table→HTML), Tier 2: OsModal ✅, Rest ausstehend.**
|
||||
**✅ Phase 0-3 abgeschlossen. Phase 4: Tier 1 + Tier A ✅, Tier B ✅ (Chip→OsBadge, Tag→OsBadge, Grid→HTML, Number→OsNumber, Radio→HTML, Table→HTML), Tier 2: OsModal ✅, ds-form entkoppelt ✅, Rest ausstehend (OsInput, OsMenu, OsSelect).**
|
||||
|
||||
---
|
||||
|
||||
@ -948,14 +949,14 @@ interface OsDropdownProps {
|
||||
| — | ds-space | ✅ → div + Margin-Utility-Klassen |
|
||||
| — | ds-flex, ds-flex-item | ✅ → HTML + CSS @media Queries |
|
||||
|
||||
### Tier B: Einfache ds-* Migration (60%)
|
||||
### Tier B: Einfache ds-* Migration ✅
|
||||
|
||||
| # | Komponente | Dateien | Ziel | Status |
|
||||
|---|------------|---------|------|--------|
|
||||
| 5 | **OsBadge** | — | ds-chip (20 Nutzungen, 5 Dateien) + ds-tag (3 Dateien) | ✅ |
|
||||
| — | ds-grid / ds-grid-item | 10 | CSS Grid (Plain HTML) | ✅ |
|
||||
| — | ds-number | 5 | `<div class="ds-number">` | ⬜ |
|
||||
| — | ds-radio | 1 | native `<input type="radio">` | ⬜ |
|
||||
| — | ds-number | 5 | OsNumber (UI-Library) | ✅ |
|
||||
| — | ds-radio | 1 | native `<input type="radio">` | ✅ |
|
||||
|
||||
### Tier 2: Layout & Feedback
|
||||
|
||||
@ -964,7 +965,7 @@ interface OsDropdownProps {
|
||||
| 5 | **OsModal** | 7 | OsButton | ✅ |
|
||||
| 6 | **OsDropdown** | — | OsButton | ⬜ |
|
||||
| 7 | **OsAvatar** | — | - | ⬜ |
|
||||
| 8 | **OsInput** | 23 | gekoppelt mit ds-form (18 Dateien) | ⬜ |
|
||||
| 8 | **OsInput** | 23 | ds-form Kopplung aufgelöst (formValidation Mixin) | ⬜ |
|
||||
|
||||
### Tier 3: Navigation (ausstehend)
|
||||
|
||||
@ -979,7 +980,7 @@ interface OsDropdownProps {
|
||||
|---|------------|---------|
|
||||
| 11 | OsSelect | 3 |
|
||||
| 12 | OsTable | 7 |
|
||||
| 13 | ds-form → HTML `<form>` oder OsForm | 18 |
|
||||
| 13 | ~~ds-form~~ | — | ✅ entkoppelt via formValidation Mixin (async-validator) |
|
||||
|
||||
> **Hinweis:** OsHeading, OsText, OsTag sind nicht mehr geplant — wurden zu Plain HTML migriert (Tier A).
|
||||
|
||||
@ -1014,17 +1015,17 @@ ds-flex, ds-flex-item ✅ → HTML + CSS @media Q
|
||||
ds-chip → OsBadge (UI-Library) ✅
|
||||
ds-tag → OsBadge shape="square" (UI-Library) ✅
|
||||
ds-grid / ds-grid-item → CSS Grid (HTML) ✅
|
||||
ds-number → Plain HTML ⬜ (5 Dateien)
|
||||
ds-radio → native <input type="radio"> ⬜ (1 Datei)
|
||||
ds-number → OsNumber (UI-Library) ✅
|
||||
ds-radio → native <input type="radio"> ✅ (1 Datei)
|
||||
```
|
||||
|
||||
### Tier 2-4: UI-Library (ausstehend)
|
||||
### Tier 2-4: UI-Library
|
||||
|
||||
```
|
||||
5. OsModal → Basis: DsModal, Feature-Modals bleiben in Webapp
|
||||
5. OsModal → Basis: DsModal, Feature-Modals bleiben in Webapp ✅
|
||||
6. OsDropdown → Basis: Dropdown (Webapp) — wichtiger als gedacht!
|
||||
7. OsAvatar → Vereint: DsAvatar + ProfileAvatar
|
||||
8. OsInput → Basis: DsInput, gekoppelt mit ds-form
|
||||
8. OsInput → Basis: DsInput (ds-form Kopplung aufgelöst via formValidation Mixin)
|
||||
9. OsMenu → Basis: DsMenu/DsMenuItem
|
||||
10. OsSelect → Basis: DsSelect
|
||||
11. OsTable → Basis: DsTable
|
||||
@ -1047,8 +1048,8 @@ ds-radio → native <input type="radio"> ⬜ (1 Datei)
|
||||
|
||||
### Noch offen:
|
||||
1. **Logo** - Existiert doppelt (Webapp + Styleguide)
|
||||
2. **Modal** - Existiert doppelt (Webapp Modal.vue ist Modal-Router, DsModal ist UI)
|
||||
3. **ds-form Kopplung** - ds-input und ds-form sind stark gekoppelt (Schema-Validation)
|
||||
2. ~~**Modal** - Existiert doppelt~~ → OsModal migriert ✅
|
||||
3. ~~**ds-form Kopplung**~~ → aufgelöst via formValidation Mixin (async-validator), vuelidate entfernt ✅
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -289,7 +289,8 @@ ds-chip + ds-tag → OsBadge (UI-Library): ✅
|
||||
- [x] 0 Tier-A `ds-*` Komponenten-Tags verbleibend
|
||||
|
||||
**Verbleibende ds-* Komponenten (6 Typen):**
|
||||
- Tier C (→ UI-Library): ds-input (23), ds-form (18), ds-modal (7), ds-menu/ds-menu-item (17), ds-select (3)
|
||||
- Tier C (→ UI-Library): ds-input (23), ds-modal (7→✅ OsModal), ds-menu/ds-menu-item (17), ds-select (3)
|
||||
- ✅ ds-form (18 Dateien) → formValidation Mixin (async-validator), vuelidate entfernt
|
||||
|
||||
**Zuvor abgeschlossen (Session 26 - CodeRabbit Review Fixes):**
|
||||
- [x] Cypress: `.os-card .title` → `.os-card > .title` (Kind-Kombinator statt Nachfahren)
|
||||
@ -415,7 +416,8 @@ ds-chip + ds-tag → OsBadge (UI-Library): ✅
|
||||
- [ ] Tier B (Rest): ds-radio → Plain HTML
|
||||
- [x] OsModal Komponente + DsModal/ConfirmModal/ReportModal → OsModal Webapp-Integration ✅
|
||||
- [ ] Weitere Tier 2 Komponenten (OsDropdown, OsAvatar, OsInput)
|
||||
- [ ] ds-form + ds-input → OsForm + OsInput (stark gekoppelt, 18+23 Dateien)
|
||||
- [x] ds-form → formValidation Mixin (async-validator), 18 Dateien migriert, vuelidate entfernt ✅
|
||||
- [ ] ds-input → OsInput (23 Dateien, ds-form Kopplung aufgelöst)
|
||||
- [ ] ds-menu / ds-menu-item → OsMenu / OsMenuItem
|
||||
- [ ] ds-select → OsSelect
|
||||
- [ ] Browser-Fehler untersuchen: `TypeError: Cannot read properties of undefined (reading 'heartO')` (ocelotIcons undefined im Browser trotz korrekter Webpack-Aliase)
|
||||
@ -686,13 +688,13 @@ Jeder migrierte Button muss manuell geprüft werden: Normal, Hover, Focus, Activ
|
||||
- [x] ds-tag (3 Dateien) → OsBadge shape="square" (UI-Library) ✅
|
||||
- [x] ds-number (5 Dateien) → OsNumber (UI-Library) ✅ + CountTo.vue gelöscht, vue-count-to entfernt
|
||||
- [x] ds-grid / ds-grid-item (10 Dateien) → CSS Grid ✅
|
||||
- [ ] ds-radio (1 Datei) → native `<input type="radio">`
|
||||
- [x] ds-radio (1 Datei) → native `<input type="radio">` ✅
|
||||
|
||||
**Tier 2: Layout & Feedback (UI-Library)**
|
||||
- [x] OsModal (Basis: DsModal → h() Render-Function, Vue 2/3 Compat, Focus-Trap, Scroll-Lock, A11y) ✅
|
||||
- [ ] OsDropdown (Basis: Webapp Dropdown)
|
||||
- [ ] OsAvatar (vereint DsAvatar + ProfileAvatar)
|
||||
- [ ] OsInput (Basis: DsInput, 23 Dateien — gekoppelt mit ds-form)
|
||||
- [ ] OsInput (Basis: DsInput, 23 Dateien — ds-form Kopplung aufgelöst via formValidation Mixin)
|
||||
|
||||
**Tier 3: Navigation (UI-Library)**
|
||||
- [ ] OsMenu (Basis: DsMenu, 11 Dateien)
|
||||
@ -701,7 +703,7 @@ Jeder migrierte Button muss manuell geprüft werden: Normal, Hover, Focus, Activ
|
||||
**Tier 4: Spezial-Komponenten**
|
||||
- [ ] OsSelect (3 Dateien)
|
||||
- [x] ds-table (7 Dateien) → Plain HTML `<table>` + CSS-Klassen ✅ (kein OsTable nötig)
|
||||
- [ ] ds-form → Plain HTML `<form>` oder OsForm (18 Dateien)
|
||||
- [x] ds-form → formValidation Mixin (async-validator), vuelidate entfernt ✅
|
||||
|
||||
**Infrastruktur**
|
||||
- [x] System-Icons einrichten ✅ vite-svg-icon Plugin, 3 System-Icons, Ocelot-Icons Entry-Point
|
||||
@ -1703,11 +1705,11 @@ Bei der Migration werden:
|
||||
| 2026-02-11 | **Milestone 4b abgeschlossen** | icon ✅, circle ✅, loading ✅ — alle OsButton-Props implementiert |
|
||||
| 2026-02-11 | **Milestone 4c: 59 Buttons** | Chat (2), AddChatRoomByUserSearch (1), CommentCard (1), CommentForm (2), ComponentSlider (2), ContributionForm (1), DeleteData (1), EmbedComponent (1), FilterMenu (1), HeaderButton (2), CategoriesFilter (2), OrderByFilter (2), EventsByFilter (2), FollowingFilter (3), GroupButton (1), ConfirmModal (2), ReportModal (2), Password/Change (1), PasswordReset/Request (1), PasswordReset/ChangePassword (1), Registration/Signup (1), ReleaseModal (1), ImageUploader (2), CreateInvitation (1), Invitation (2), ProfileList (1), ReportRow (1), MySomethingList (3), ActionButton (1), pages/index (2), profile/add-post (1), post/blur-toggle (1), groups/slug (3), settings/index (1), admin/users (2), blocked-users (1), data-download (1), muted-users (1), groups/index (1), enter-nonce (1) |
|
||||
| 2026-02-11 | **type="submit" Pattern** | OsButton hat `type="button"` als Default; alle Form-Submit-Buttons brauchen explizit `type="submit"` |
|
||||
| 2026-02-11 | **!!errors Pattern** | DsForm `errors` ist ein Objekt, nicht Boolean; OsButton `disabled` Prop erwartet Boolean → `!!errors` nötig |
|
||||
| 2026-02-11 | **!!errors Pattern** | `formErrors` ist ein Objekt, nicht Boolean; OsButton `disabled` Prop erwartet Boolean → `!!formErrors` nötig |
|
||||
| 2026-02-11 | **CSS-Selector Pattern** | `.base-button` → `> button` oder `button`; Position/Dimensions brauchen `!important` für Tailwind-Override |
|
||||
| 2026-02-11 | **Disabled border-color** | Outline disabled border von `var(--color-disabled)` auf `var(--color-disabled-border,#e5e3e8)` mit Fallback |
|
||||
| 2026-02-11 | **Phase 3 abgeschlossen** | 132 `<os-button>` Tags in 78 Dateien, 0 `<base-button>` in Templates verbleibend |
|
||||
| 2026-02-11 | **Password/Change.vue Fix** | `!!errors` für disabled-Prop (DsForm errors ist Objekt) |
|
||||
| 2026-02-11 | **Password/Change.vue Fix** | `!!errors` für disabled-Prop (formErrors ist Objekt) |
|
||||
| 2026-02-11 | **CommentForm.vue Fix** | `type="submit"` fehlte + `!!errors` für disabled-Prop |
|
||||
| 2026-02-11 | **GroupForm.vue ds-button** | Letzter `<ds-button>` in Webapp → `<os-button>` mit `#icon` Slot migriert |
|
||||
| 2026-02-11 | **OsButton.spec.ts TS-Fix** | `size` aus `Object.entries` als Union Type gecastet (`as 'sm' | 'md' | 'lg' | 'xl'`) |
|
||||
@ -1847,6 +1849,9 @@ Bei der Migration werden:
|
||||
| 2026-03-13 | **Modal Webapp-Integration** | ConfirmModal + ReportModal nutzen OsModal; Vuex Modal Store entfernt; Modals inline gerendert |
|
||||
| 2026-03-13 | **Modal Bugfixes** | z-index Stacking Context Fix (PostTeaser/GroupTeaser), Callback-Promise Propagation (ReportList, MySomethingList), Group Leave Authorization Fix ($nextTick), Cypress .ds-modal → .os-modal |
|
||||
| 2026-03-13 | **Modal A11y** | scrollable-region-focusable Fix (tabindex=0), aria-label Fallback wenn kein Title, body overflow save/restore |
|
||||
| 2026-03-14 | **ds-form entkoppelt** | Neues `formValidation` Mixin (async-validator): provide/subscribe Pattern, formData/formSchema/formErrors, handleInput/handleInputValid Callbacks; vuelidate komplett entfernt |
|
||||
| 2026-03-14 | **18 Formulare migriert** | CommentForm, ContributionForm, EnterNonce, GroupForm, Password/Change, PasswordReset (2), Registration (5), Signup, MySomethingList, donations, admin/users, settings (3) |
|
||||
| 2026-03-20 | **formValidation Fix** | `handleInput()` vor `$validateForm()` aufrufen (Reihenfolge-Bug: handleInput überschrieb handleInputValid bei synchronem async-validator Callback) |
|
||||
|
||||
---
|
||||
|
||||
@ -1865,12 +1870,11 @@ Bei der Migration werden:
|
||||
| Status | Komponenten |
|
||||
|--------|------------|
|
||||
| ✅ UI-Library | OsButton, OsIcon, OsSpinner, OsCard, OsBadge, OsNumber, OsModal (7) |
|
||||
| ✅ → Plain HTML | Section, Placeholder, List, ListItem, Container, Heading, Text, Space, Flex, FlexItem, Grid, GridItem, Table (13) — Tier A/B |
|
||||
| ✅ → Plain HTML | Section, Placeholder, List, ListItem, Container, Heading, Text, Space, Flex, FlexItem, Grid, GridItem, Table, Radio (14) — Tier A/B |
|
||||
| ✅ → UI-Library | Chip, Tag → OsBadge (2), Number → OsNumber (1) — Tier B |
|
||||
| ⬜ → Plain HTML | Radio (1) — Tier B |
|
||||
| ✅ ds-form entkoppelt | Form-Validierung → formValidation Mixin (async-validator), vuelidate entfernt, ds-input/ds-select bleiben als UI-Komponenten |
|
||||
| ⬜ → UI-Library | Input, Menu, MenuItem, Select (4) — Tier 2-3 |
|
||||
| ⬜ Nicht genutzt | Code, CopyField, FormItem, InputError, InputLabel, Page, PageTitle, Logo, Avatar, TableCol, TableHeadCol (11) |
|
||||
| ⬜ Offen | Form (18 Dateien — HTML `<form>` oder OsForm?) |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -1,34 +1,32 @@
|
||||
<template>
|
||||
<ds-form v-model="form" @submit="handleSubmit" class="comment-form">
|
||||
<template #default="{ errors }">
|
||||
<os-card>
|
||||
<hc-editor ref="editor" :users="users" :value="form.content" @input="updateEditorContent" />
|
||||
<div class="buttons">
|
||||
<os-button
|
||||
variant="primary"
|
||||
appearance="outline"
|
||||
:disabled="disabled && !update"
|
||||
@click="handleCancel"
|
||||
data-test="cancel-button"
|
||||
>
|
||||
{{ $t('actions.cancel') }}
|
||||
</os-button>
|
||||
<os-button
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
type="submit"
|
||||
:loading="loading"
|
||||
:disabled="disabled || !!errors"
|
||||
>
|
||||
<template #icon>
|
||||
<os-icon :icon="icons.comment" />
|
||||
</template>
|
||||
{{ $t('post.comment.submit') }}
|
||||
</os-button>
|
||||
</div>
|
||||
</os-card>
|
||||
</template>
|
||||
</ds-form>
|
||||
<form @submit.prevent="handleSubmit" class="comment-form" novalidate>
|
||||
<os-card>
|
||||
<hc-editor ref="editor" :users="users" :value="form.content" @input="updateEditorContent" />
|
||||
<div class="buttons">
|
||||
<os-button
|
||||
variant="primary"
|
||||
appearance="outline"
|
||||
:disabled="disabled && !update"
|
||||
@click="handleCancel"
|
||||
data-test="cancel-button"
|
||||
>
|
||||
{{ $t('actions.cancel') }}
|
||||
</os-button>
|
||||
<os-button
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
type="submit"
|
||||
:loading="loading"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<template #icon>
|
||||
<os-icon :icon="icons.comment" />
|
||||
</template>
|
||||
{{ $t('post.comment.submit') }}
|
||||
</os-button>
|
||||
</div>
|
||||
</os-card>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@ -1,13 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<ds-form
|
||||
class="contribution-form"
|
||||
ref="contributionForm"
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@submit="submit"
|
||||
>
|
||||
<template #default="{ errors }">
|
||||
<form class="contribution-form" @submit.prevent="onSubmit" novalidate>
|
||||
<template>
|
||||
<os-card>
|
||||
<template #heroImage>
|
||||
<img
|
||||
@ -42,10 +36,10 @@
|
||||
<os-badge
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
:variant="errors && errors.title ? 'danger' : undefined"
|
||||
:variant="formErrors && formErrors.title ? 'danger' : undefined"
|
||||
>
|
||||
{{ formData.title.length }}/{{ formSchema.title.max }}
|
||||
<os-icon v-if="errors && errors.title" :icon="icons.warning" />
|
||||
<os-icon v-if="formErrors && formErrors.title" :icon="icons.warning" />
|
||||
</os-badge>
|
||||
<editor
|
||||
:users="users"
|
||||
@ -56,10 +50,10 @@
|
||||
<os-badge
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
:variant="errors && errors.content ? 'danger' : undefined"
|
||||
:variant="formErrors && formErrors.content ? 'danger' : undefined"
|
||||
>
|
||||
{{ contentLength }}
|
||||
<os-icon v-if="errors && errors.content" :icon="icons.warning" />
|
||||
<os-icon v-if="formErrors && formErrors.content" :icon="icons.warning" />
|
||||
</os-badge>
|
||||
|
||||
<!-- Eventdata -->
|
||||
@ -86,13 +80,13 @@
|
||||
></date-picker>
|
||||
</div>
|
||||
<div
|
||||
v-if="errors && errors.eventStart"
|
||||
v-if="formErrors && formErrors.eventStart"
|
||||
class="chipbox event-grid-item-margin-helper"
|
||||
>
|
||||
<os-badge
|
||||
role="alert"
|
||||
aria-live="assertive"
|
||||
:variant="errors && errors.eventStart ? 'danger' : undefined"
|
||||
:variant="formErrors && formErrors.eventStart ? 'danger' : undefined"
|
||||
>
|
||||
<os-icon :icon="icons.warning" />
|
||||
</os-badge>
|
||||
@ -129,10 +123,10 @@
|
||||
<os-badge
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
:variant="errors && errors.eventVenue ? 'danger' : undefined"
|
||||
:variant="formErrors && formErrors.eventVenue ? 'danger' : undefined"
|
||||
>
|
||||
{{ formData.eventVenue.length }}/{{ formSchema.eventVenue.max }}
|
||||
<os-icon v-if="errors && errors.eventVenue" :icon="icons.warning" />
|
||||
<os-icon v-if="formErrors && formErrors.eventVenue" :icon="icons.warning" />
|
||||
</os-badge>
|
||||
</div>
|
||||
</div>
|
||||
@ -146,10 +140,13 @@
|
||||
<os-badge
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
:variant="errors && errors.eventLocationName ? 'danger' : undefined"
|
||||
:variant="formErrors && formErrors.eventLocationName ? 'danger' : undefined"
|
||||
>
|
||||
{{ formData.eventLocationName.length }}/{{ formSchema.eventLocationName.max }}
|
||||
<os-icon v-if="errors && errors.eventLocationName" :icon="icons.warning" />
|
||||
<os-icon
|
||||
v-if="formErrors && formErrors.eventLocationName"
|
||||
:icon="icons.warning"
|
||||
/>
|
||||
</os-badge>
|
||||
</div>
|
||||
</div>
|
||||
@ -177,10 +174,10 @@
|
||||
v-if="categoriesActive"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
:variant="errors && errors.categoryIds ? 'danger' : undefined"
|
||||
:variant="formErrors && formErrors.categoryIds ? 'danger' : undefined"
|
||||
>
|
||||
{{ formData.categoryIds.length }} / 3
|
||||
<os-icon v-if="errors && errors.categoryIds" :icon="icons.warning" />
|
||||
<os-icon v-if="formErrors && formErrors.categoryIds" :icon="icons.warning" />
|
||||
</os-badge>
|
||||
<div class="ds-flex ds-flex-gap-xxx-small buttons-footer">
|
||||
<div style="flex: 3.5 0 0" class="buttons-footer-helper">
|
||||
@ -207,7 +204,7 @@
|
||||
appearance="filled"
|
||||
type="submit"
|
||||
:loading="loading"
|
||||
:disabled="!!errors"
|
||||
:disabled="!!formErrors"
|
||||
>
|
||||
<template #icon>
|
||||
<os-icon :icon="icons.check" />
|
||||
@ -218,7 +215,7 @@
|
||||
</div>
|
||||
</os-card>
|
||||
</template>
|
||||
</ds-form>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
@ -235,9 +232,10 @@ import PageParamsLink from '~/components/_new/features/PageParamsLink/PageParams
|
||||
import DatePicker from 'vue2-datepicker'
|
||||
import 'vue2-datepicker/scss/index.scss'
|
||||
import GetCategories from '~/mixins/getCategoriesMixin.js'
|
||||
import formValidation from '~/mixins/formValidation'
|
||||
|
||||
export default {
|
||||
mixins: [GetCategories],
|
||||
mixins: [GetCategories, formValidation],
|
||||
components: {
|
||||
CategoriesSelect,
|
||||
DatePicker,
|
||||
@ -424,6 +422,9 @@ export default {
|
||||
notBeforeEvent(date) {
|
||||
return date <= new Date(this.formData.eventStart)
|
||||
},
|
||||
onSubmit() {
|
||||
this.formSubmit(this.submit)
|
||||
},
|
||||
submit() {
|
||||
let image = null
|
||||
|
||||
@ -470,16 +471,16 @@ export default {
|
||||
})
|
||||
},
|
||||
updateEditorContent(value) {
|
||||
this.$refs.contributionForm.update('content', value)
|
||||
this.updateFormField('content', value)
|
||||
},
|
||||
changeEventIsOnline(event) {
|
||||
this.$refs.contributionForm.update('eventIsOnline', this.formData.eventIsOnline)
|
||||
this.updateFormField('eventIsOnline', this.formData.eventIsOnline)
|
||||
},
|
||||
changeEventEnd(event) {
|
||||
this.$refs.contributionForm.update('eventEnd', event)
|
||||
this.updateFormField('eventEnd', event)
|
||||
},
|
||||
changeEventStart(event) {
|
||||
this.$refs.contributionForm.update('eventStart', event)
|
||||
this.updateFormField('eventStart', event)
|
||||
},
|
||||
addHeroImage(file) {
|
||||
this.formData.image = null
|
||||
|
||||
@ -1,12 +1,5 @@
|
||||
<template>
|
||||
<ds-form
|
||||
class="enter-nonce"
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@submit="handleSubmitVerify"
|
||||
@input="handleInput"
|
||||
@input-valid="handleInputValid"
|
||||
>
|
||||
<form class="enter-nonce" @submit.prevent="onSubmit" novalidate>
|
||||
<ds-input
|
||||
:placeholder="$t('components.registration.email-nonce.form.nonce')"
|
||||
model="nonce"
|
||||
@ -30,15 +23,17 @@
|
||||
{{ $t('components.registration.email-nonce.form.next') }}
|
||||
</os-button>
|
||||
<slot></slot>
|
||||
</ds-form>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { OsButton } from '@ocelot-social/ui'
|
||||
import registrationConstants from '~/constants/registration'
|
||||
import formValidation from '~/mixins/formValidation'
|
||||
|
||||
export default {
|
||||
name: 'EnterNonce',
|
||||
mixins: [formValidation],
|
||||
components: { OsButton },
|
||||
props: {
|
||||
email: { type: String, required: true },
|
||||
@ -69,6 +64,9 @@ export default {
|
||||
async handleInputValid() {
|
||||
this.disabled = false
|
||||
},
|
||||
onSubmit() {
|
||||
this.formSubmit(this.handleSubmitVerify)
|
||||
},
|
||||
handleSubmitVerify() {
|
||||
const { nonce } = this.formData
|
||||
const email = this.email
|
||||
|
||||
@ -1,14 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<ds-form
|
||||
class="group-form"
|
||||
ref="groupForm"
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@submit="submit"
|
||||
>
|
||||
<!-- "errors" is only working if you use a submit event on the form -->
|
||||
<template #default="{ errors }">
|
||||
<form class="group-form" @submit.prevent="onSubmit" novalidate>
|
||||
<template>
|
||||
<!-- group Name -->
|
||||
<ds-input
|
||||
name="name"
|
||||
@ -20,10 +13,10 @@
|
||||
<os-badge
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
:variant="errors && errors.name ? 'danger' : undefined"
|
||||
:variant="formErrors && formErrors.name ? 'danger' : undefined"
|
||||
>
|
||||
{{ `${formData.name.length} / ${formSchema.name.min}–${formSchema.name.max}` }}
|
||||
<os-icon v-if="errors && errors.name" :icon="icons.warning" />
|
||||
<os-icon v-if="formErrors && formErrors.name" :icon="icons.warning" />
|
||||
</os-badge>
|
||||
|
||||
<!-- group Slug -->
|
||||
@ -56,11 +49,13 @@
|
||||
<os-badge
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
:variant="errors && errors.groupType && formData.groupType === '' ? 'danger' : undefined"
|
||||
:variant="
|
||||
formErrors && formErrors.groupType && formData.groupType === '' ? 'danger' : undefined
|
||||
"
|
||||
>
|
||||
{{ `${formData.groupType === '' ? 0 : 1} / 1` }}
|
||||
<os-icon
|
||||
v-if="errors && errors.groupType && formData.groupType === ''"
|
||||
v-if="formErrors && formErrors.groupType && formData.groupType === ''"
|
||||
:icon="icons.warning"
|
||||
/>
|
||||
</os-badge>
|
||||
@ -91,10 +86,10 @@
|
||||
<os-badge
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
:variant="errors && errors.description ? 'danger' : undefined"
|
||||
:variant="formErrors && formErrors.description ? 'danger' : undefined"
|
||||
>
|
||||
{{ `${descriptionLength} / ${formSchema.description.min}` }}
|
||||
<os-icon v-if="errors && errors.description" :icon="icons.warning" />
|
||||
<os-icon v-if="formErrors && formErrors.description" :icon="icons.warning" />
|
||||
</os-badge>
|
||||
|
||||
<!-- actionRadius -->
|
||||
@ -109,12 +104,14 @@
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
:variant="
|
||||
errors && errors.actionRadius && formData.actionRadius === '' ? 'danger' : undefined
|
||||
formErrors && formErrors.actionRadius && formData.actionRadius === ''
|
||||
? 'danger'
|
||||
: undefined
|
||||
"
|
||||
>
|
||||
{{ `${formData.actionRadius === '' ? 0 : 1} / 1` }}
|
||||
<os-icon
|
||||
v-if="errors && errors.actionRadius && formData.actionRadius === ''"
|
||||
v-if="formErrors && formErrors.actionRadius && formData.actionRadius === ''"
|
||||
:icon="icons.warning"
|
||||
/>
|
||||
</os-badge>
|
||||
@ -138,10 +135,10 @@
|
||||
<os-badge
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
:variant="errors && errors.categoryIds ? 'danger' : undefined"
|
||||
:variant="formErrors && formErrors.categoryIds ? 'danger' : undefined"
|
||||
>
|
||||
{{ formData.categoryIds.length }} / 3
|
||||
<os-icon v-if="errors && errors.categoryIds" :icon="icons.warning" />
|
||||
<os-icon v-if="formErrors && formErrors.categoryIds" :icon="icons.warning" />
|
||||
</os-badge>
|
||||
</div>
|
||||
<!-- submit -->
|
||||
@ -153,14 +150,14 @@
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
type="submit"
|
||||
:disabled="checkFormError(errors)"
|
||||
:disabled="checkFormError(formErrors)"
|
||||
>
|
||||
<template #icon><os-icon :icon="icons.save" /></template>
|
||||
{{ update ? $t('group.update') : $t('group.save') }}
|
||||
</os-button>
|
||||
</div>
|
||||
</template>
|
||||
</ds-form>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -178,10 +175,11 @@ import Editor from '~/components/Editor/Editor'
|
||||
import ActionRadiusSelect from '~/components/Select/ActionRadiusSelect'
|
||||
import LocationSelect from '~/components/Select/LocationSelect'
|
||||
import GetCategories from '~/mixins/getCategoriesMixin.js'
|
||||
import formValidation from '~/mixins/formValidation'
|
||||
|
||||
export default {
|
||||
name: 'GroupForm',
|
||||
mixins: [GetCategories],
|
||||
mixins: [GetCategories, formValidation],
|
||||
components: {
|
||||
CategoriesSelect,
|
||||
Editor,
|
||||
@ -308,16 +306,19 @@ export default {
|
||||
return false
|
||||
},
|
||||
changeGroupType(event) {
|
||||
this.$refs.groupForm.update('groupType', event.target.value)
|
||||
this.updateFormField('groupType', event.target.value)
|
||||
},
|
||||
changeActionRadius(event) {
|
||||
this.$refs.groupForm.update('actionRadius', event.target.value)
|
||||
this.updateFormField('actionRadius', event.target.value)
|
||||
},
|
||||
changeLocation(event) {
|
||||
this.formData.locationName = event.target.value
|
||||
},
|
||||
updateEditorDescription(value) {
|
||||
this.$refs.groupForm.update('description', value)
|
||||
this.updateFormField('description', value)
|
||||
},
|
||||
onSubmit() {
|
||||
this.formSubmit(this.submit)
|
||||
},
|
||||
submit() {
|
||||
const { name, slug, about, description, groupType, actionRadius, categoryIds } = this.formData
|
||||
|
||||
@ -65,8 +65,7 @@ describe('ChangePassword.vue', () => {
|
||||
it('displays a validation error', async () => {
|
||||
await wrapper.find('form').trigger('submit')
|
||||
await wrapper.vm.$nextTick()
|
||||
const dsForm = wrapper.findComponent({ name: 'DsForm' })
|
||||
expect(dsForm.vm.errors).toHaveProperty('passwordConfirmation')
|
||||
expect(wrapper.vm.formErrors).toHaveProperty('passwordConfirmation')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -1,44 +1,42 @@
|
||||
<template>
|
||||
<ds-form v-model="formData" :schema="formSchema" @submit="handleSubmit">
|
||||
<template #default="{ errors }">
|
||||
<ds-input
|
||||
id="oldPassword"
|
||||
model="oldPassword"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:label="$t('settings.security.change-password.label-old-password')"
|
||||
/>
|
||||
<ds-input
|
||||
id="password"
|
||||
model="password"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:label="$t('settings.security.change-password.label-new-password')"
|
||||
/>
|
||||
<ds-input
|
||||
id="passwordConfirmation"
|
||||
model="passwordConfirmation"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:label="$t('settings.security.change-password.label-new-password-confirm')"
|
||||
/>
|
||||
<password-strength :password="formData.password" />
|
||||
<div class="ds-mt-base ds-mb-large">
|
||||
<os-button
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
:loading="loading"
|
||||
:disabled="!!errors"
|
||||
type="submit"
|
||||
>
|
||||
<template #icon>
|
||||
<os-icon :icon="icons.lock" />
|
||||
</template>
|
||||
{{ $t('settings.security.change-password.button') }}
|
||||
</os-button>
|
||||
</div>
|
||||
</template>
|
||||
</ds-form>
|
||||
<form @submit.prevent="onSubmit" novalidate>
|
||||
<ds-input
|
||||
id="oldPassword"
|
||||
model="oldPassword"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:label="$t('settings.security.change-password.label-old-password')"
|
||||
/>
|
||||
<ds-input
|
||||
id="password"
|
||||
model="password"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:label="$t('settings.security.change-password.label-new-password')"
|
||||
/>
|
||||
<ds-input
|
||||
id="passwordConfirmation"
|
||||
model="passwordConfirmation"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:label="$t('settings.security.change-password.label-new-password-confirm')"
|
||||
/>
|
||||
<password-strength :password="formData.password" />
|
||||
<div class="ds-mt-base ds-mb-large">
|
||||
<os-button
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
:loading="loading"
|
||||
:disabled="!!formErrors"
|
||||
type="submit"
|
||||
>
|
||||
<template #icon>
|
||||
<os-icon :icon="icons.lock" />
|
||||
</template>
|
||||
{{ $t('settings.security.change-password.button') }}
|
||||
</os-button>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -47,9 +45,11 @@ import { iconRegistry } from '~/utils/iconRegistry'
|
||||
import gql from 'graphql-tag'
|
||||
import PasswordStrength from './Strength'
|
||||
import PasswordForm from '~/components/utils/PasswordFormHelper'
|
||||
import formValidation from '~/mixins/formValidation'
|
||||
|
||||
export default {
|
||||
name: 'ChangePassword',
|
||||
mixins: [formValidation],
|
||||
components: {
|
||||
OsButton,
|
||||
OsIcon,
|
||||
@ -77,6 +77,9 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSubmit() {
|
||||
this.formSubmit(this.handleSubmit)
|
||||
},
|
||||
async handleSubmit(data) {
|
||||
this.loading = true
|
||||
const mutation = gql`
|
||||
|
||||
@ -1,44 +1,41 @@
|
||||
<template>
|
||||
<div class="ds-mt-base ds-mb-xxx-small">
|
||||
<ds-form
|
||||
<form
|
||||
v-if="!changePasswordResult"
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@submit="handleSubmitPassword"
|
||||
@submit.prevent="onSubmit"
|
||||
class="change-password"
|
||||
novalidate
|
||||
>
|
||||
<template #default="{ errors }">
|
||||
<ds-input
|
||||
id="password"
|
||||
model="password"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:label="$t('settings.security.change-password.label-new-password')"
|
||||
/>
|
||||
<ds-input
|
||||
id="passwordConfirmation"
|
||||
model="passwordConfirmation"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:label="$t('settings.security.change-password.label-new-password-confirm')"
|
||||
/>
|
||||
<password-strength :password="formData.password" />
|
||||
<div class="ds-mt-base ds-mb-xxx-small">
|
||||
<os-button
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
:loading="$apollo.loading"
|
||||
:disabled="!!errors"
|
||||
type="submit"
|
||||
>
|
||||
<template #icon>
|
||||
<os-icon :icon="icons.lock" />
|
||||
</template>
|
||||
{{ $t('settings.security.change-password.button') }}
|
||||
</os-button>
|
||||
</div>
|
||||
</template>
|
||||
</ds-form>
|
||||
<ds-input
|
||||
id="password"
|
||||
model="password"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:label="$t('settings.security.change-password.label-new-password')"
|
||||
/>
|
||||
<ds-input
|
||||
id="passwordConfirmation"
|
||||
model="passwordConfirmation"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:label="$t('settings.security.change-password.label-new-password-confirm')"
|
||||
/>
|
||||
<password-strength :password="formData.password" />
|
||||
<div class="ds-mt-base ds-mb-xxx-small">
|
||||
<os-button
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
:loading="$apollo.loading"
|
||||
:disabled="!!formErrors"
|
||||
type="submit"
|
||||
>
|
||||
<template #icon>
|
||||
<os-icon :icon="icons.lock" />
|
||||
</template>
|
||||
{{ $t('settings.security.change-password.button') }}
|
||||
</os-button>
|
||||
</div>
|
||||
</form>
|
||||
<div v-else class="ds-mb-large">
|
||||
<template v-if="changePasswordResult === 'success'">
|
||||
<transition name="ds-transition-fade">
|
||||
@ -75,8 +72,10 @@ import PasswordStrength from '../Password/Strength'
|
||||
import gql from 'graphql-tag'
|
||||
import { SweetalertIcon } from 'vue-sweetalert-icons'
|
||||
import PasswordForm from '~/components/utils/PasswordFormHelper'
|
||||
import formValidation from '~/mixins/formValidation'
|
||||
|
||||
export default {
|
||||
mixins: [formValidation],
|
||||
components: {
|
||||
OsButton,
|
||||
OsIcon,
|
||||
@ -105,6 +104,9 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSubmit() {
|
||||
this.formSubmit(this.handleSubmitPassword)
|
||||
},
|
||||
async handleSubmitPassword() {
|
||||
const mutation = gql`
|
||||
mutation ($nonce: String!, $email: String!, $password: String!) {
|
||||
|
||||
@ -1,12 +1,5 @@
|
||||
<template>
|
||||
<ds-form
|
||||
v-if="!submitted"
|
||||
@input="handleInput"
|
||||
@input-valid="handleInputValid"
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@submit="handleSubmit"
|
||||
>
|
||||
<form v-if="!submitted" @submit.prevent="onSubmit" novalidate>
|
||||
<div class="ds-my-small">
|
||||
<ds-input
|
||||
:placeholder="$t('login.email')"
|
||||
@ -32,7 +25,7 @@
|
||||
{{ $t('components.password-reset.request.form.submit') }}
|
||||
</os-button>
|
||||
<slot></slot>
|
||||
</ds-form>
|
||||
</form>
|
||||
<div v-else>
|
||||
<transition name="ds-transition-fade">
|
||||
<div class="ds-flex ds-flex-centered">
|
||||
@ -47,8 +40,10 @@
|
||||
import { OsButton } from '@ocelot-social/ui'
|
||||
import gql from 'graphql-tag'
|
||||
import { SweetalertIcon } from 'vue-sweetalert-icons'
|
||||
import formValidation from '~/mixins/formValidation'
|
||||
|
||||
export default {
|
||||
mixins: [formValidation],
|
||||
components: {
|
||||
OsButton,
|
||||
SweetalertIcon,
|
||||
@ -82,6 +77,9 @@ export default {
|
||||
handleInputValid() {
|
||||
this.disabled = false
|
||||
},
|
||||
onSubmit() {
|
||||
this.formSubmit(this.handleSubmit)
|
||||
},
|
||||
async handleSubmit() {
|
||||
const mutation = gql`
|
||||
mutation ($email: String!, $locale: String!) {
|
||||
|
||||
@ -25,15 +25,7 @@
|
||||
<div class="ds-my-xxx-small"></div>
|
||||
</div>
|
||||
<div v-else class="create-account-card">
|
||||
<ds-form
|
||||
class="create-user-account"
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@input="handleInput"
|
||||
@input-valid="handleInputValid"
|
||||
>
|
||||
<!-- leave this here in case the scoped variable is needed in the future nobody would remember this -->
|
||||
<!-- <template v-slot="{ errors }"> -->
|
||||
<form class="create-user-account" novalidate>
|
||||
<template>
|
||||
<email-display-and-verify :email="sliderData.collectedInputData.email" />
|
||||
|
||||
@ -151,7 +143,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<div class="ds-my-xxx-small"></div>
|
||||
</ds-form>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -168,11 +160,13 @@ import PasswordForm from '~/components/utils/PasswordFormHelper'
|
||||
import { iconRegistry } from '~/utils/iconRegistry'
|
||||
import ShowPassword from '../ShowPassword/ShowPassword.vue'
|
||||
import LocationSelect from '~/components/Select/LocationSelect'
|
||||
import formValidation from '~/mixins/formValidation'
|
||||
|
||||
const threePerEmSpace = ' ' // unicode u+2004;
|
||||
|
||||
export default {
|
||||
name: 'RegistrationSlideCreate',
|
||||
mixins: [formValidation],
|
||||
components: {
|
||||
EmailDisplayAndVerify,
|
||||
PageParamsLink,
|
||||
|
||||
@ -1,11 +1,5 @@
|
||||
<template>
|
||||
<ds-form
|
||||
class="enter-email"
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@input="handleInput"
|
||||
@input-valid="handleInputValid"
|
||||
>
|
||||
<form class="enter-email" @submit.prevent novalidate>
|
||||
<p class="ds-text">
|
||||
{{ $t('components.registration.signup.form.description') }}
|
||||
</p>
|
||||
@ -23,7 +17,7 @@
|
||||
</label>
|
||||
</p>
|
||||
<div class="ds-my-xxx-small"></div>
|
||||
</ds-form>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -31,6 +25,7 @@ import gql from 'graphql-tag'
|
||||
import metadata from '~/constants/metadata'
|
||||
import { isEmail } from 'validator'
|
||||
import translateErrorMessage from '~/components/utils/TranslateErrorMessage'
|
||||
import formValidation from '~/mixins/formValidation'
|
||||
|
||||
export const SignupMutation = gql`
|
||||
mutation ($email: String!, $locale: String!, $inviteCode: String) {
|
||||
@ -41,6 +36,7 @@ export const SignupMutation = gql`
|
||||
`
|
||||
export default {
|
||||
name: 'RegistrationSlideEmail',
|
||||
mixins: [formValidation],
|
||||
props: {
|
||||
sliderData: { type: Object, required: true },
|
||||
},
|
||||
|
||||
@ -1,11 +1,5 @@
|
||||
<template>
|
||||
<ds-form
|
||||
class="enter-invite"
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@input="handleInput"
|
||||
@input-valid="handleInputValid"
|
||||
>
|
||||
<form class="enter-invite" @submit.prevent novalidate>
|
||||
<ds-input
|
||||
:placeholder="formSchema.inviteCode.placeholder"
|
||||
:minlength="formSchema.inviteCode.minLength"
|
||||
@ -40,16 +34,18 @@
|
||||
</div>
|
||||
<slot></slot>
|
||||
<div class="ds-my-xxx-small"></div>
|
||||
</ds-form>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import registrationConstants from '~/constants/registrationBranded.js'
|
||||
import { validateInviteCode } from '~/graphql/InviteCode'
|
||||
import ProfileAvatar from '~/components/_new/generic/ProfileAvatar/ProfileAvatar'
|
||||
import formValidation from '~/mixins/formValidation'
|
||||
|
||||
export default {
|
||||
name: 'RegistrationSlideInvite',
|
||||
mixins: [formValidation],
|
||||
props: {
|
||||
sliderData: { type: Object, required: true },
|
||||
},
|
||||
|
||||
@ -1,12 +1,5 @@
|
||||
<template>
|
||||
<ds-form
|
||||
class="enter-nonce"
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@submit="handleSubmitVerify"
|
||||
@input="handleInput"
|
||||
@input-valid="handleInputValid"
|
||||
>
|
||||
<form class="enter-nonce" @submit.prevent="onSubmit" novalidate>
|
||||
<email-display-and-verify :email="sliderData.collectedInputData.email" />
|
||||
<ds-input
|
||||
:placeholder="$t('components.registration.email-nonce.form.nonce')"
|
||||
@ -19,13 +12,14 @@
|
||||
</p>
|
||||
<slot></slot>
|
||||
<div class="ds-my-xxx-small"></div>
|
||||
</ds-form>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import gql from 'graphql-tag'
|
||||
import { isEmail } from 'validator'
|
||||
import registrationConstants from '~/constants/registration'
|
||||
import formValidation from '~/mixins/formValidation'
|
||||
|
||||
import EmailDisplayAndVerify from './EmailDisplayAndVerify'
|
||||
|
||||
@ -36,6 +30,7 @@ export const verifyNonceQuery = gql`
|
||||
`
|
||||
export default {
|
||||
name: 'RegistrationSlideNonce',
|
||||
mixins: [formValidation],
|
||||
components: {
|
||||
EmailDisplayAndVerify,
|
||||
},
|
||||
@ -101,6 +96,9 @@ export default {
|
||||
async handleInputValid() {
|
||||
this.sendValidation()
|
||||
},
|
||||
onSubmit() {
|
||||
this.formSubmit(this.handleSubmitVerify)
|
||||
},
|
||||
async handleSubmitVerify() {
|
||||
const { email, nonce } = this.sliderData.collectedInputData
|
||||
const variables = { email, nonce }
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
<template>
|
||||
<div v-if="!data && !error" class="ds-my-large">
|
||||
<ds-form
|
||||
@input="handleInput"
|
||||
@input-valid="handleInputValid"
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@submit="handleSubmit"
|
||||
>
|
||||
<form @submit.prevent="onSubmit" novalidate>
|
||||
<h1>
|
||||
{{
|
||||
invitation
|
||||
@ -40,7 +34,7 @@
|
||||
{{ $t('components.registration.signup.form.submit') }}
|
||||
</os-button>
|
||||
<slot></slot>
|
||||
</ds-form>
|
||||
</form>
|
||||
</div>
|
||||
<div v-else class="ds-my-large">
|
||||
<template v-if="!error">
|
||||
@ -67,6 +61,7 @@ import gql from 'graphql-tag'
|
||||
import metadata from '~/constants/metadata'
|
||||
import { SweetalertIcon } from 'vue-sweetalert-icons'
|
||||
import translateErrorMessage from '~/components/utils/TranslateErrorMessage'
|
||||
import formValidation from '~/mixins/formValidation'
|
||||
|
||||
export const SignupMutation = gql`
|
||||
mutation ($email: String!, $locale: String!, $inviteCode: String) {
|
||||
@ -77,6 +72,7 @@ export const SignupMutation = gql`
|
||||
`
|
||||
export default {
|
||||
name: 'Signup',
|
||||
mixins: [formValidation],
|
||||
components: {
|
||||
OsButton,
|
||||
SweetalertIcon,
|
||||
@ -115,6 +111,9 @@ export default {
|
||||
handleInputValid() {
|
||||
this.disabled = false
|
||||
},
|
||||
onSubmit() {
|
||||
this.formSubmit(this.handleSubmit)
|
||||
},
|
||||
async handleSubmit() {
|
||||
const { email } = this.formData
|
||||
|
||||
|
||||
@ -1,11 +1,5 @@
|
||||
<template>
|
||||
<ds-form
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@input="handleInput"
|
||||
@input-valid="handleInputValid"
|
||||
@submit="handleSubmitItem"
|
||||
>
|
||||
<form @submit.prevent="onSubmit" novalidate>
|
||||
<div v-if="isEditing">
|
||||
<div class="ds-my-base">
|
||||
<h5 class="ds-heading ds-heading-h5">
|
||||
@ -77,16 +71,18 @@
|
||||
</os-button>
|
||||
</div>
|
||||
<confirm-modal v-if="showConfirmModal" :modalData="currentModalData" @close="closeModal" />
|
||||
</ds-form>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { OsButton, OsIcon } from '@ocelot-social/ui'
|
||||
import { iconRegistry } from '~/utils/iconRegistry'
|
||||
import ConfirmModal from '~/components/Modal/ConfirmModal'
|
||||
import formValidation from '~/mixins/formValidation'
|
||||
|
||||
export default {
|
||||
name: 'MySomethingList',
|
||||
mixins: [formValidation],
|
||||
components: { ConfirmModal, OsButton, OsIcon },
|
||||
props: {
|
||||
useFormData: { type: Object, default: () => ({}) },
|
||||
@ -139,6 +135,9 @@ export default {
|
||||
this.icons = iconRegistry
|
||||
},
|
||||
methods: {
|
||||
onSubmit() {
|
||||
this.formSubmit(this.handleSubmitItem)
|
||||
},
|
||||
handleInput(data) {
|
||||
this.callbacks.handleInput(this, data)
|
||||
this.disabled = true
|
||||
|
||||
@ -2,6 +2,29 @@ import { debounce } from 'lodash'
|
||||
import { checkSlugAvailableQuery } from '~/graphql/User.js'
|
||||
|
||||
export default function UniqueSlugForm({ translate, apollo, currentUser }) {
|
||||
let pendingCallback = null
|
||||
|
||||
const debouncedSlugCheck = debounce((value, callback) => {
|
||||
const variables = { slug: value }
|
||||
apollo
|
||||
.query({ query: checkSlugAvailableQuery, variables })
|
||||
.then((response) => {
|
||||
const {
|
||||
data: { User },
|
||||
} = response
|
||||
const existingSlug = User && User[0] && User[0].slug
|
||||
const available = !existingSlug || existingSlug === currentUser.slug
|
||||
if (!available) {
|
||||
callback(new Error(translate('settings.validation.slug.alreadyTaken')))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
callback()
|
||||
})
|
||||
}, 500)
|
||||
|
||||
return {
|
||||
formSchema: {
|
||||
slug: [
|
||||
@ -13,21 +36,20 @@ export default function UniqueSlugForm({ translate, apollo, currentUser }) {
|
||||
},
|
||||
{
|
||||
asyncValidator(rule, value, callback) {
|
||||
debounce(() => {
|
||||
const variables = { slug: value }
|
||||
apollo.query({ query: checkSlugAvailableQuery, variables }).then((response) => {
|
||||
const {
|
||||
data: { User },
|
||||
} = response
|
||||
const existingSlug = User && User[0] && User[0].slug
|
||||
const available = !existingSlug || existingSlug === currentUser.slug
|
||||
if (!available) {
|
||||
callback(new Error(translate('settings.validation.slug.alreadyTaken')))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
})
|
||||
}, 500)()
|
||||
// Resolve any pending callback from a previous debounced call
|
||||
// that was cancelled, so async-validator doesn't hang
|
||||
if (pendingCallback) {
|
||||
pendingCallback()
|
||||
}
|
||||
pendingCallback = callback
|
||||
debouncedSlugCheck(value, (error) => {
|
||||
pendingCallback = null
|
||||
if (error) {
|
||||
callback(error)
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
102
webapp/mixins/formValidation.js
Normal file
102
webapp/mixins/formValidation.js
Normal file
@ -0,0 +1,102 @@
|
||||
import Schema from 'async-validator'
|
||||
|
||||
Schema.warning = function () {}
|
||||
|
||||
function cloneDeep(obj) {
|
||||
return JSON.parse(JSON.stringify(obj))
|
||||
}
|
||||
|
||||
export default {
|
||||
provide() {
|
||||
return {
|
||||
$parentForm: this.$formProxy,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formErrors: null,
|
||||
}
|
||||
},
|
||||
beforeCreate() {
|
||||
const vm = this
|
||||
const subscribers = []
|
||||
this.$formProxy = {
|
||||
subscribe(cb) {
|
||||
if (cb && typeof cb === 'function') {
|
||||
cb(cloneDeep(vm.formData))
|
||||
subscribers.push(cb)
|
||||
}
|
||||
},
|
||||
unsubscribe(cb) {
|
||||
const index = subscribers.indexOf(cb)
|
||||
if (index > -1) {
|
||||
subscribers.splice(index, 1)
|
||||
}
|
||||
},
|
||||
update(model, value) {
|
||||
vm.updateFormField(model, value)
|
||||
},
|
||||
}
|
||||
this.$formSubscribers = subscribers
|
||||
},
|
||||
watch: {
|
||||
formData: {
|
||||
handler(value) {
|
||||
this.$notifyFormSubscribers(value, this.formErrors)
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateFormField(model, value) {
|
||||
this.$set(this.formData, model, value)
|
||||
if (typeof this.handleInput === 'function') {
|
||||
this.handleInput(cloneDeep(this.formData))
|
||||
}
|
||||
this.$validateForm(() => {
|
||||
if (typeof this.handleInputValid === 'function') {
|
||||
this.handleInputValid(cloneDeep(this.formData))
|
||||
}
|
||||
})
|
||||
},
|
||||
formSubmit(callback) {
|
||||
this.$validateForm(() => {
|
||||
if (callback && typeof callback === 'function') {
|
||||
callback(cloneDeep(this.formData))
|
||||
}
|
||||
})
|
||||
},
|
||||
$validateForm(cb) {
|
||||
const schema = this.formSchema
|
||||
if (!schema || Object.keys(schema).length === 0) {
|
||||
this.formErrors = null
|
||||
this.$notifyFormSubscribers(this.formData, null)
|
||||
if (cb && typeof cb === 'function') {
|
||||
cb()
|
||||
}
|
||||
return
|
||||
}
|
||||
const validator = new Schema(schema)
|
||||
validator.validate(this.formData, (errors) => {
|
||||
if (errors) {
|
||||
this.formErrors = errors.reduce((errorObj, error) => {
|
||||
const result = { ...errorObj }
|
||||
result[error.field] = error.message
|
||||
return result
|
||||
}, {})
|
||||
} else {
|
||||
this.formErrors = null
|
||||
}
|
||||
this.$notifyFormSubscribers(this.formData, this.formErrors)
|
||||
if (!errors && cb && typeof cb === 'function') {
|
||||
cb()
|
||||
}
|
||||
})
|
||||
},
|
||||
$notifyFormSubscribers(data, errors) {
|
||||
this.$formSubscribers.forEach((cb) => {
|
||||
cb(cloneDeep(data), errors)
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<os-card>
|
||||
<h2 class="title">{{ $t('admin.donations.name') }}</h2>
|
||||
<ds-form v-model="formData" @submit="submit">
|
||||
<form @submit.prevent="submit" novalidate>
|
||||
<p class="ds-text show-donations-checkbox">
|
||||
<input id="showDonations" type="checkbox" v-model="showDonations" />
|
||||
<label for="showDonations">
|
||||
@ -31,15 +31,17 @@
|
||||
<os-button class="donations-info-button" variant="primary" appearance="filled" type="submit">
|
||||
{{ $t('actions.save') }}
|
||||
</os-button>
|
||||
</ds-form>
|
||||
</form>
|
||||
</os-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { OsButton, OsCard } from '@ocelot-social/ui'
|
||||
import { DonationsQuery, UpdateDonations } from '~/graphql/Donations'
|
||||
import formValidation from '~/mixins/formValidation'
|
||||
|
||||
export default {
|
||||
mixins: [formValidation],
|
||||
components: { OsButton, OsCard },
|
||||
data() {
|
||||
return {
|
||||
|
||||
@ -14,8 +14,6 @@ exports[`Users given badges are disabled renders 1`] = `
|
||||
</h2>
|
||||
|
||||
<form
|
||||
autocomplete="off"
|
||||
class="ds-form"
|
||||
novalidate="novalidate"
|
||||
>
|
||||
<div
|
||||
@ -465,8 +463,6 @@ exports[`Users given badges are enabled renders 1`] = `
|
||||
</h2>
|
||||
|
||||
<form
|
||||
autocomplete="off"
|
||||
class="ds-form"
|
||||
novalidate="novalidate"
|
||||
>
|
||||
<div
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div class="admin-users">
|
||||
<os-card>
|
||||
<h2 class="title">{{ $t('admin.users.name') }}</h2>
|
||||
<ds-form v-model="form" @submit="submit">
|
||||
<form @submit.prevent="onSubmit" novalidate>
|
||||
<div class="ds-flex ds-flex-gap-small">
|
||||
<div style="flex: 0 0 90%; width: 90%">
|
||||
<ds-input
|
||||
@ -24,7 +24,7 @@
|
||||
</os-button>
|
||||
</div>
|
||||
</div>
|
||||
</ds-form>
|
||||
</form>
|
||||
</os-card>
|
||||
<os-card v-if="User && User.length">
|
||||
<div class="ds-table-wrap">
|
||||
@ -162,8 +162,10 @@ import { isEmail } from 'validator'
|
||||
import PaginationButtons from '~/components/_new/generic/PaginationButtons/PaginationButtons'
|
||||
import { adminUserQuery } from '~/graphql/User'
|
||||
import { FetchAllRoles, updateUserRole } from '~/graphql/admin/Roles'
|
||||
import formValidation from '~/mixins/formValidation'
|
||||
|
||||
export default {
|
||||
mixins: [formValidation],
|
||||
components: {
|
||||
OsButton,
|
||||
OsCard,
|
||||
@ -184,10 +186,8 @@ export default {
|
||||
email: null,
|
||||
filter: null,
|
||||
userRoles: [],
|
||||
form: {
|
||||
formData: {
|
||||
query: '',
|
||||
},
|
||||
formData: {
|
||||
query: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
@ -234,9 +234,9 @@ export default {
|
||||
next() {
|
||||
this.offset += this.pageSize
|
||||
},
|
||||
submit(formData) {
|
||||
onSubmit() {
|
||||
this.offset = 0
|
||||
const { query } = formData
|
||||
const { query } = this.formData
|
||||
if (isEmail(query)) {
|
||||
this.email = query
|
||||
this.filter = null
|
||||
|
||||
@ -29,7 +29,7 @@ describe('change-password', () => {
|
||||
})
|
||||
|
||||
it('renders', () => {
|
||||
expect(wrapper.findAll('.ds-form')).toHaveLength(1)
|
||||
expect(wrapper.findAll('form')).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -30,7 +30,7 @@ describe('enter-nonce.vue', () => {
|
||||
})
|
||||
|
||||
it('renders', () => {
|
||||
expect(wrapper.findAll('.ds-form')).toHaveLength(1)
|
||||
expect(wrapper.findAll('form')).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -33,7 +33,7 @@ describe('request.vue', () => {
|
||||
})
|
||||
|
||||
it('renders', () => {
|
||||
expect(wrapper.findAll('.ds-form')).toHaveLength(1)
|
||||
expect(wrapper.findAll('form')).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('navigates to enter-nonce on handlePasswordResetRequested', () => {
|
||||
|
||||
@ -114,7 +114,7 @@ describe('index.vue', () => {
|
||||
const wrapper = Wrapper()
|
||||
|
||||
wrapper.find('#name').setValue('Peter')
|
||||
wrapper.find('.ds-form').trigger('submit')
|
||||
wrapper.find('form').trigger('submit')
|
||||
|
||||
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
|
||||
})
|
||||
@ -130,7 +130,7 @@ describe('index.vue', () => {
|
||||
const wrapper = Wrapper()
|
||||
|
||||
wrapper.find('#name').setValue('Peter')
|
||||
wrapper.find('.ds-form').trigger('submit')
|
||||
wrapper.find('form').trigger('submit')
|
||||
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
@ -147,7 +147,7 @@ describe('index.vue', () => {
|
||||
const wrapper = Wrapper()
|
||||
|
||||
wrapper.find('#slug').setValue('peter-der-lustige')
|
||||
wrapper.find('.ds-form').trigger('submit')
|
||||
wrapper.find('form').trigger('submit')
|
||||
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
@ -163,7 +163,7 @@ describe('index.vue', () => {
|
||||
it('calls updateUser mutation', async () => {
|
||||
const wrapper = Wrapper()
|
||||
wrapper.findComponent(LocationSelect).vm.$emit('input', 'Berlin, Germany')
|
||||
wrapper.find('.ds-form').trigger('submit')
|
||||
wrapper.find('form').trigger('submit')
|
||||
|
||||
await expect(mocks.$apollo.mutate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
@ -180,7 +180,7 @@ describe('index.vue', () => {
|
||||
const wrapper = Wrapper()
|
||||
|
||||
wrapper.find('#about').setValue('I am Peter!111elf')
|
||||
wrapper.find('.ds-form').trigger('submit')
|
||||
wrapper.find('form').trigger('submit')
|
||||
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
@ -199,7 +199,7 @@ describe('index.vue', () => {
|
||||
wrapper.find('#slug').setValue('peter-der-lustige')
|
||||
await wrapper.findComponent(LocationSelect).vm.$emit('input', 'Hamburg, Germany')
|
||||
wrapper.find('#about').setValue('I am Peter!111elf')
|
||||
wrapper.find('.ds-form').trigger('submit')
|
||||
wrapper.find('form').trigger('submit')
|
||||
|
||||
await expect(mocks.$apollo.mutate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
|
||||
@ -1,47 +1,45 @@
|
||||
<template>
|
||||
<ds-form class="settings-form" v-model="formData" :schema="formSchema" @submit="submit">
|
||||
<template #default="{ errors }">
|
||||
<os-card>
|
||||
<h2 class="title">{{ $t('settings.data.name') }}</h2>
|
||||
<ds-input
|
||||
id="name"
|
||||
model="name"
|
||||
icon="user"
|
||||
:label="
|
||||
$env.ASK_FOR_REAL_NAME
|
||||
? $t('settings.data.realNamePlease')
|
||||
: $t('settings.data.labelName')
|
||||
"
|
||||
:placeholder="$t('settings.data.namePlaceholder')"
|
||||
/>
|
||||
<ds-input id="slug" model="slug" icon="at" :label="$t('settings.data.labelSlug')" />
|
||||
<location-select
|
||||
class="location-selet"
|
||||
v-model="formData.locationName"
|
||||
:canBeCleared="!$env.REQUIRE_LOCATION"
|
||||
/>
|
||||
<!-- eslint-enable vue/use-v-on-exact -->
|
||||
<ds-input
|
||||
id="about"
|
||||
model="about"
|
||||
type="textarea"
|
||||
rows="3"
|
||||
:label="$t('settings.data.labelBio')"
|
||||
:placeholder="$t('settings.data.labelBio')"
|
||||
/>
|
||||
<os-button
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
type="submit"
|
||||
:disabled="!!errors"
|
||||
:loading="loadingData"
|
||||
>
|
||||
<template #icon><os-icon :icon="icons.check" /></template>
|
||||
{{ $t('actions.save') }}
|
||||
</os-button>
|
||||
</os-card>
|
||||
</template>
|
||||
</ds-form>
|
||||
<form class="settings-form" @submit.prevent="onSubmit" novalidate>
|
||||
<os-card>
|
||||
<h2 class="title">{{ $t('settings.data.name') }}</h2>
|
||||
<ds-input
|
||||
id="name"
|
||||
model="name"
|
||||
icon="user"
|
||||
:label="
|
||||
$env.ASK_FOR_REAL_NAME
|
||||
? $t('settings.data.realNamePlease')
|
||||
: $t('settings.data.labelName')
|
||||
"
|
||||
:placeholder="$t('settings.data.namePlaceholder')"
|
||||
/>
|
||||
<ds-input id="slug" model="slug" icon="at" :label="$t('settings.data.labelSlug')" />
|
||||
<location-select
|
||||
class="location-selet"
|
||||
v-model="formData.locationName"
|
||||
:canBeCleared="!$env.REQUIRE_LOCATION"
|
||||
/>
|
||||
<!-- eslint-enable vue/use-v-on-exact -->
|
||||
<ds-input
|
||||
id="about"
|
||||
model="about"
|
||||
type="textarea"
|
||||
rows="3"
|
||||
:label="$t('settings.data.labelBio')"
|
||||
:placeholder="$t('settings.data.labelBio')"
|
||||
/>
|
||||
<os-button
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
type="submit"
|
||||
:disabled="!!formErrors"
|
||||
:loading="loadingData"
|
||||
>
|
||||
<template #icon><os-icon :icon="icons.check" /></template>
|
||||
{{ $t('actions.save') }}
|
||||
</os-button>
|
||||
</os-card>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -52,9 +50,10 @@ import UniqueSlugForm from '~/components/utils/UniqueSlugForm'
|
||||
import LocationSelect from '~/components/Select/LocationSelect'
|
||||
import { updateUserMutation } from '~/graphql/User'
|
||||
import scrollToContent from './scroll-to-content.js'
|
||||
import formValidation from '~/mixins/formValidation'
|
||||
|
||||
export default {
|
||||
mixins: [scrollToContent],
|
||||
mixins: [scrollToContent, formValidation],
|
||||
name: 'Settings',
|
||||
components: {
|
||||
OsButton,
|
||||
@ -105,6 +104,9 @@ export default {
|
||||
...mapMutations({
|
||||
setCurrentUser: 'auth/SET_USER',
|
||||
}),
|
||||
onSubmit() {
|
||||
this.formSubmit(this.submit)
|
||||
},
|
||||
async submit() {
|
||||
this.loadingData = true
|
||||
const { name, slug, about } = this.formData
|
||||
|
||||
@ -1,58 +1,59 @@
|
||||
<template>
|
||||
<ds-form v-model="form" :schema="formSchema" @submit="submit">
|
||||
<template #default="{ errors }">
|
||||
<os-card>
|
||||
<h2 class="title">{{ $t('settings.email.name') }}</h2>
|
||||
<ds-input
|
||||
id="email"
|
||||
model="email"
|
||||
icon="envelope"
|
||||
disabled
|
||||
:label="$t('settings.email.labelNewEmail')"
|
||||
/>
|
||||
<ds-input
|
||||
id="nonce"
|
||||
model="nonce"
|
||||
icon="question-circle"
|
||||
:label="$t('settings.email.labelNonce')"
|
||||
/>
|
||||
<os-button variant="primary" appearance="filled" type="submit" :disabled="!!errors">
|
||||
<template #icon><os-icon :icon="icons.check" /></template>
|
||||
{{ $t('actions.save') }}
|
||||
</os-button>
|
||||
</os-card>
|
||||
</template>
|
||||
</ds-form>
|
||||
<form @submit.prevent="onSubmit" novalidate>
|
||||
<os-card>
|
||||
<h2 class="title">{{ $t('settings.email.name') }}</h2>
|
||||
<ds-input
|
||||
id="email"
|
||||
model="email"
|
||||
icon="envelope"
|
||||
disabled
|
||||
:label="$t('settings.email.labelNewEmail')"
|
||||
/>
|
||||
<ds-input
|
||||
id="nonce"
|
||||
model="nonce"
|
||||
icon="question-circle"
|
||||
:label="$t('settings.email.labelNonce')"
|
||||
/>
|
||||
<os-button variant="primary" appearance="filled" type="submit" :disabled="!!formErrors">
|
||||
<template #icon><os-icon :icon="icons.check" /></template>
|
||||
{{ $t('actions.save') }}
|
||||
</os-button>
|
||||
</os-card>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { OsButton, OsCard, OsIcon } from '@ocelot-social/ui'
|
||||
import { iconRegistry } from '~/utils/iconRegistry'
|
||||
import formValidation from '~/mixins/formValidation'
|
||||
|
||||
export default {
|
||||
mixins: [formValidation],
|
||||
components: { OsButton, OsCard, OsIcon },
|
||||
data() {
|
||||
return {
|
||||
formData: {
|
||||
email: '',
|
||||
nonce: '',
|
||||
},
|
||||
formSchema: {
|
||||
nonce: { type: 'string', required: true },
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
form: {
|
||||
get: function () {
|
||||
const { email = '', nonce = '' } = this.$route.query
|
||||
return { email, nonce }
|
||||
},
|
||||
set: function (formData) {
|
||||
this.formData = formData
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
const { email = '', nonce = '' } = this.$route.query
|
||||
this.formData.email = email
|
||||
this.formData.nonce = nonce
|
||||
},
|
||||
created() {
|
||||
this.icons = iconRegistry
|
||||
},
|
||||
methods: {
|
||||
onSubmit() {
|
||||
this.formSubmit(this.submit)
|
||||
},
|
||||
async submit() {
|
||||
const { email, nonce } = this.formData
|
||||
this.$router.replace({
|
||||
|
||||
@ -5,34 +5,27 @@
|
||||
</transition>
|
||||
<p class="ds-text" v-html="submitMessage" />
|
||||
</os-card>
|
||||
<ds-form v-else v-model="form" :schema="formSchema" @submit="submit">
|
||||
<template #default="{ errors }">
|
||||
<os-card>
|
||||
<h2 class="title">{{ $t('settings.email.name') }}</h2>
|
||||
<ds-input
|
||||
id="email"
|
||||
model="email"
|
||||
icon="envelope"
|
||||
:label="$t('settings.email.labelEmail')"
|
||||
/>
|
||||
<div class="ds-mb-large backendErrors" v-if="backendErrors">
|
||||
<p class="ds-text ds-text-center ds-text-bold ds-text-danger">
|
||||
{{ backendErrors.message }}
|
||||
</p>
|
||||
</div>
|
||||
<os-button
|
||||
:disabled="!!errors"
|
||||
:loading="loadingData"
|
||||
type="submit"
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
>
|
||||
<template #icon><os-icon :icon="icons.check" /></template>
|
||||
{{ $t('actions.save') }}
|
||||
</os-button>
|
||||
</os-card>
|
||||
</template>
|
||||
</ds-form>
|
||||
<form v-else @submit.prevent="onSubmit" novalidate>
|
||||
<os-card>
|
||||
<h2 class="title">{{ $t('settings.email.name') }}</h2>
|
||||
<ds-input id="email" model="email" icon="envelope" :label="$t('settings.email.labelEmail')" />
|
||||
<div class="ds-mb-large backendErrors" v-if="backendErrors">
|
||||
<p class="ds-text ds-text-center ds-text-bold ds-text-danger">
|
||||
{{ backendErrors.message }}
|
||||
</p>
|
||||
</div>
|
||||
<os-button
|
||||
:disabled="!!formErrors"
|
||||
:loading="loadingData"
|
||||
type="submit"
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
>
|
||||
<template #icon><os-icon :icon="icons.check" /></template>
|
||||
{{ $t('actions.save') }}
|
||||
</os-button>
|
||||
</os-card>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -42,9 +35,10 @@ import { iconRegistry } from '~/utils/iconRegistry'
|
||||
import { AddEmailAddressMutation } from '~/graphql/EmailAddress.js'
|
||||
import { SweetalertIcon } from 'vue-sweetalert-icons'
|
||||
import scrollToContent from '../scroll-to-content.js'
|
||||
import formValidation from '~/mixins/formValidation'
|
||||
|
||||
export default {
|
||||
mixins: [scrollToContent],
|
||||
mixins: [scrollToContent, formValidation],
|
||||
components: {
|
||||
OsButton,
|
||||
OsCard,
|
||||
@ -59,8 +53,14 @@ export default {
|
||||
backendErrors: null,
|
||||
data: null,
|
||||
loadingData: false,
|
||||
formData: {
|
||||
email: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.formData.email = this.currentUser.email || ''
|
||||
},
|
||||
computed: {
|
||||
submitMessage() {
|
||||
const { email } = this.data.AddEmailAddress
|
||||
@ -69,15 +69,6 @@ export default {
|
||||
...mapGetters({
|
||||
currentUser: 'auth/user',
|
||||
}),
|
||||
form: {
|
||||
get: function () {
|
||||
const { email } = this.currentUser
|
||||
return { email }
|
||||
},
|
||||
set: function (formData) {
|
||||
this.formData = formData
|
||||
},
|
||||
},
|
||||
formSchema() {
|
||||
const currentEmail = this.currentUser.email
|
||||
const sameEmailValidationError = this.$t('settings.email.validation.same-email')
|
||||
@ -98,6 +89,9 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onSubmit() {
|
||||
this.formSubmit(this.submit)
|
||||
},
|
||||
async submit() {
|
||||
this.loadingData = true
|
||||
const { email } = this.formData
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user