mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 01:46:07 +00:00
create, edit, delete project branding
This commit is contained in:
parent
b3290a8191
commit
c160c39378
@ -55,7 +55,8 @@
|
||||
"vue-router": "4.4.0",
|
||||
"vue3-datepicker": "^0.4.0",
|
||||
"vuex": "4.1.0",
|
||||
"vuex-persistedstate": "4.1.0"
|
||||
"vuex-persistedstate": "4.1.0",
|
||||
"yup": "^1.6.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@apollo/client": "^3.10.8",
|
||||
|
||||
@ -56,7 +56,7 @@ export default {
|
||||
? formatDistanceToNow(new Date(dateString), {
|
||||
includeSecond: true,
|
||||
addSuffix: true,
|
||||
locale: useDateLocale,
|
||||
locale: useDateLocale(),
|
||||
})
|
||||
: ''
|
||||
},
|
||||
|
||||
@ -32,6 +32,9 @@
|
||||
<BNavItem to="/federation" :active="isActive('federation')">
|
||||
{{ $t('navbar.instances') }}
|
||||
</BNavItem>
|
||||
<BNavItem to="/projectBranding" :active="isActive('projectBranding')" :title="$t('navbar.projectBrandingTooltip')">
|
||||
{{ $t('navbar.projectBranding') }}
|
||||
</BNavItem>
|
||||
<BNavItem to="/statistic" :active="isActive('statistic')">
|
||||
{{ $t('navbar.statistic') }}
|
||||
</BNavItem>
|
||||
|
||||
129
admin/src/components/ProjectBranding/ProjectBrandingForm.vue
Normal file
129
admin/src/components/ProjectBranding/ProjectBrandingForm.vue
Normal file
@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<div class="project-branding-form">
|
||||
<BForm @submit.prevent="submit">
|
||||
<ValidatedInput
|
||||
:model-value="name"
|
||||
name="name"
|
||||
:label="$t('name')"
|
||||
:rules="validationSchema.fields.name"
|
||||
class="mb-3"
|
||||
@update:model-value="updateField"
|
||||
/>
|
||||
<ValidatedInput
|
||||
:model-value="alias"
|
||||
name="alias"
|
||||
:label="$t('alias')"
|
||||
:rules="validationSchema.fields.alias"
|
||||
class="mb-3"
|
||||
@update:model-value="updateField"
|
||||
/>
|
||||
<ValidatedInput
|
||||
:model-value="description"
|
||||
name="description"
|
||||
:label="$t('description')"
|
||||
:rules="validationSchema.fields.description"
|
||||
textarea="true"
|
||||
class="mb-3"
|
||||
@update:model-value="updateField"
|
||||
/>
|
||||
<BFormGroup
|
||||
:label="$t('projectBranding.newUserToSpace')"
|
||||
label-for="newUserToSpace-input-field"
|
||||
class="mb-3"
|
||||
>
|
||||
<BFormCheckbox
|
||||
id="newUserToSpace-input-field"
|
||||
:model-value="newUserToSpace"
|
||||
name="newUserToSpace"
|
||||
value="true"
|
||||
unchecked-value="false"
|
||||
@update:model-value="(value) => updateField(value, 'newUserToSpace')"
|
||||
>
|
||||
{{ $t('projectBranding.newUserToSpaceTooltip') }}
|
||||
</BFormCheckbox>
|
||||
</BFormGroup>
|
||||
<ValidatedInput
|
||||
:model-value="logoUrl"
|
||||
name="logoUrl"
|
||||
:label="$t('logo')"
|
||||
:rules="validationSchema.fields.logoUrl"
|
||||
class="mb-3"
|
||||
@update:model-value="updateField"
|
||||
/>
|
||||
<BFormInvalidFeedback v-if="errorMessage" class="d-block mb-3">
|
||||
{{ errorMessage }}
|
||||
</BFormInvalidFeedback>
|
||||
<div class="d-flex gap-2">
|
||||
<BButton type="submit" variant="primary">{{ $t('save') }}</BButton>
|
||||
<BButton type="reset" variant="secondary" @click="resetForm">{{ $t('reset') }}</BButton>
|
||||
</div>
|
||||
</BForm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import ValidatedInput from '@/components/input/ValidatedInput'
|
||||
import { reactive, computed, watch, ref } from 'vue'
|
||||
import { object, string, boolean } from 'yup'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: Object, required: true },
|
||||
})
|
||||
|
||||
const form = reactive({ ...props.modelValue })
|
||||
const errorMessage = ref('')
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => Object.assign(form, newValue),
|
||||
)
|
||||
const name = computed(() => form.name)
|
||||
const alias = computed(() => form.alias)
|
||||
const description = computed(() => form.description)
|
||||
const newUserToSpace = computed(() => form.newUserToSpace)
|
||||
const logoUrl = computed(() => form.logoUrl)
|
||||
|
||||
const validationSchema = object({
|
||||
name: string().min(3).max(255).required(),
|
||||
alias: string()
|
||||
.matches(/^[a-z0-9-_]+$/, {
|
||||
message: 'Alias can only contain lowercase letters, numbers, hyphens, and underscores.',
|
||||
})
|
||||
.min(3)
|
||||
.max(32)
|
||||
.required(),
|
||||
description: string().nullable().optional(),
|
||||
newUserToSpace: boolean().optional(),
|
||||
logoUrl: string().url('Logo URL must be a valid URL.').nullable().optional(),
|
||||
})
|
||||
|
||||
function updateField(value, name) {
|
||||
form[name] = value
|
||||
}
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
function submit() {
|
||||
validationSchema
|
||||
.validate(form, { stripUnknown: true })
|
||||
.then((cleanedForm) => {
|
||||
emit('update:modelValue', { ...cleanedForm, id: props.modelValue.id })
|
||||
})
|
||||
.catch((err) => {
|
||||
errorMessage.value = err.message
|
||||
})
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
if (props.modelValue.id === undefined) {
|
||||
Object.assign(form, {
|
||||
name: '',
|
||||
alias: '',
|
||||
description: undefined,
|
||||
newUserToSpace: false,
|
||||
logoUrl: undefined,
|
||||
})
|
||||
return
|
||||
} else {
|
||||
Object.assign(form, props.modelValue)
|
||||
}
|
||||
errorMessage.value = ''
|
||||
}
|
||||
</script>
|
||||
69
admin/src/components/ProjectBranding/ProjectBrandingItem.vue
Normal file
69
admin/src/components/ProjectBranding/ProjectBrandingItem.vue
Normal file
@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div class="project-branding-item">
|
||||
<BRow :title="item.description" @click="details = !details">
|
||||
<BCol cols="3">{{ item.name }}</BCol>
|
||||
<BCol cols="2">{{ item.alias }}</BCol>
|
||||
<BCol cols="2">{{ item.newUserToSpace }}</BCol>
|
||||
<BCol cols="3"><img :src="item.logoUrl" :alt="item.logoUrl" /></BCol>
|
||||
<BCol cols="1">
|
||||
<BButton v-b-tooltip.hover variant="danger" :title="$t('delete')" @click.stop="deleteItem">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</BButton>
|
||||
</BCol>
|
||||
</BRow>
|
||||
<BRow v-if="details || item.id === undefined" class="details">
|
||||
<BCol colspan="5">
|
||||
<BCard>
|
||||
<ProjectBrandingForm :model-value="item" @update:model-value="update" />
|
||||
</BCard>
|
||||
</BCol>
|
||||
</BRow>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, toRefs } from 'vue'
|
||||
import ProjectBrandingForm from './ProjectBrandingForm.vue'
|
||||
import { useMutation } from '@vue/apollo-composable'
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
const props = defineProps({
|
||||
item: { type: Object, required: true },
|
||||
})
|
||||
const { item } = toRefs(props)
|
||||
const details = ref(false)
|
||||
const emit = defineEmits(['update:item', 'deleted:item'])
|
||||
|
||||
function update(form) {
|
||||
const { mutate } = useMutation(gql`
|
||||
mutation upsertProjectBranding($input: ProjectBrandingInput!) {
|
||||
upsertProjectBranding(input: $input) {
|
||||
id
|
||||
name
|
||||
alias
|
||||
newUserToSpace
|
||||
logoUrl
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
mutate({
|
||||
input: { ...form },
|
||||
}).then(({ data }) => {
|
||||
emit('update:item', data.upsertProjectBranding)
|
||||
})
|
||||
}
|
||||
function deleteItem() {
|
||||
const { mutate } = useMutation(gql`
|
||||
mutation deleteProjectBranding($id: ID!) {
|
||||
deleteProjectBranding(id: $id)
|
||||
}
|
||||
`)
|
||||
|
||||
mutate({
|
||||
id: item.value.id,
|
||||
}).then(() => {
|
||||
emit('deleted:item', item.value.id)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
45
admin/src/components/input/LabeledInput.vue
Normal file
45
admin/src/components/input/LabeledInput.vue
Normal file
@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div :class="wrapperClassName">
|
||||
<BFormGroup :label="label" :label-for="labelFor">
|
||||
<BFormTextarea
|
||||
v-if="textarea"
|
||||
v-bind="{ ...$attrs, id: labelFor, name }"
|
||||
v-model="model"
|
||||
trim
|
||||
:rows="4"
|
||||
:max-rows="4"
|
||||
no-resize
|
||||
/>
|
||||
<BFormInput v-else v-bind="{ ...$attrs, id: labelFor, name }" v-model="model" />
|
||||
<slot></slot>
|
||||
</BFormGroup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, defineOptions, defineModel } from 'vue'
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
textarea: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const model = defineModel()
|
||||
|
||||
const wrapperClassName = computed(() => (props.name ? `input-${props.name}` : 'input'))
|
||||
const labelFor = computed(() => `${props.name}-input-field`)
|
||||
</script>
|
||||
90
admin/src/components/input/ValidatedInput.vue
Normal file
90
admin/src/components/input/ValidatedInput.vue
Normal file
@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<LabeledInput
|
||||
v-bind="$attrs"
|
||||
:min="minValue"
|
||||
:max="maxValue"
|
||||
:model-value="model"
|
||||
:reset-value="resetValue"
|
||||
:locale="$i18n.locale"
|
||||
:required="!isOptional"
|
||||
:label="label"
|
||||
:name="name"
|
||||
:state="valid"
|
||||
@update:model-value="updateValue"
|
||||
>
|
||||
<BFormInvalidFeedback v-if="errorMessage">
|
||||
{{ errorMessage }}
|
||||
</BFormInvalidFeedback>
|
||||
</LabeledInput>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import LabeledInput from './LabeledInput'
|
||||
import { translateYupErrorString } from '@/validationSchemas'
|
||||
|
||||
const props = defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
modelValue: [String, Number, Date],
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
rules: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const model = ref(props.modelValue)
|
||||
|
||||
const valid = computed(() => {
|
||||
if (
|
||||
(props.modelValue === undefined || props.modelValue === '' || props.modelValue === null) &&
|
||||
isOptional.value
|
||||
) {
|
||||
return null
|
||||
}
|
||||
return props.rules.isValidSync(props.modelValue)
|
||||
})
|
||||
const errorMessage = computed(() => {
|
||||
if (props.modelValue === undefined || props.modelValue === '' || props.modelValue === null) {
|
||||
return undefined
|
||||
}
|
||||
try {
|
||||
props.rules.validateSync(props.modelValue)
|
||||
return undefined
|
||||
} catch (e) {
|
||||
return translateYupErrorString(e.message, t)
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const updateValue = (newValue) => {
|
||||
emit('update:modelValue', newValue, props.name, valid.value)
|
||||
}
|
||||
|
||||
// update model and if value changed and model isn't null, check validation,
|
||||
// for loading Input with existing value and show correct validation state
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
model.value = props.modelValue
|
||||
},
|
||||
)
|
||||
|
||||
// extract additional parameter like min and max from schema
|
||||
const schemaDescription = computed(() => props.rules.describe())
|
||||
const getTestParameter = (name) =>
|
||||
schemaDescription.value?.tests?.find((t) => t.name === name)?.params[name]
|
||||
const minValue = computed(() => getTestParameter('min'))
|
||||
const maxValue = computed(() => getTestParameter('max'))
|
||||
const resetValue = computed(() => schemaDescription.value.default)
|
||||
const isOptional = computed(() => schemaDescription.value.optional)
|
||||
</script>
|
||||
@ -1,5 +1,7 @@
|
||||
{
|
||||
"GDD": "GDD",
|
||||
"actions": "Aktionen",
|
||||
"alias": "Alias",
|
||||
"all_emails": "Alle Nutzer",
|
||||
"back": "zurück",
|
||||
"change_user_role": "Nutzerrolle ändern",
|
||||
@ -52,7 +54,6 @@
|
||||
"enter_text": "Text eintragen",
|
||||
"form": "Schöpfungsformular",
|
||||
"min_characters": "Mindestens 10 Zeichen eingeben",
|
||||
"reset": "Zurücksetzen",
|
||||
"select_month": "Monat auswählen",
|
||||
"select_value": "Betrag auswählen",
|
||||
"submit_creation": "Schöpfung einreichen",
|
||||
@ -68,6 +69,7 @@
|
||||
"deleted": "gelöscht",
|
||||
"deleted_user": "Alle gelöschten Nutzer",
|
||||
"deny": "Ablehnen",
|
||||
"description": "Beschreibung",
|
||||
"e_mail": "E-Mail",
|
||||
"edit": "bearbeiten",
|
||||
"enabled": "aktiviert",
|
||||
@ -127,6 +129,7 @@
|
||||
"lastname": "Nachname",
|
||||
"latitude": "Breitengrad:",
|
||||
"latitude-longitude-smart": "Breitengrad, Längengrad",
|
||||
"logo": "Logo",
|
||||
"longitude": "Längengrad:",
|
||||
"math": {
|
||||
"equals": "=",
|
||||
@ -155,6 +158,8 @@
|
||||
"instances": "Instanzen",
|
||||
"logout": "Abmelden",
|
||||
"my-account": "Mein Konto",
|
||||
"projectBranding": "Projekt Branding",
|
||||
"projectBrandingTooltip": "Nutze ein eigenes Logo im Gradido Login und füge neue Benutzer einem Humhub-Space hinzu",
|
||||
"statistic": "Statistik",
|
||||
"user_search": "Nutzersuche"
|
||||
},
|
||||
@ -199,8 +204,15 @@
|
||||
"yes": "Ja, Nutzer wiederherstellen"
|
||||
}
|
||||
},
|
||||
"projectBranding": {
|
||||
"addTooltip": "Neuen Projekt Branding Eintrag hinzufügen",
|
||||
"title": "Projekt Brandings",
|
||||
"newUserToSpace": "Benutzer hinzufügen?",
|
||||
"newUserToSpaceTooltip": "Neue Benutzer automatisch zum Space hinzufügen, falls Space vorhanden"
|
||||
},
|
||||
"redeemed": "eingelöst",
|
||||
"removeNotSelf": "Als Admin/Moderator kannst du dich nicht selber löschen.",
|
||||
"reset": "Zurücksetzen",
|
||||
"save": "Speichern",
|
||||
"statistic": {
|
||||
"activeUsers": "Aktive Mitglieder",
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
{
|
||||
"GDD": "GDD",
|
||||
"actions": "Actions",
|
||||
"alias": "Alias",
|
||||
"all_emails": "All users",
|
||||
"back": "back",
|
||||
"change_user_role": "Change user role",
|
||||
@ -68,6 +70,7 @@
|
||||
"deleted": "deleted",
|
||||
"deleted_user": "All deleted user",
|
||||
"deny": "Reject",
|
||||
"description": "Description",
|
||||
"e_mail": "E-mail",
|
||||
"edit": "edit",
|
||||
"enabled": "enabled",
|
||||
@ -127,6 +130,7 @@
|
||||
"lastname": "Lastname",
|
||||
"latitude": "Latitude:",
|
||||
"latitude-longitude-smart": "Latitude, Longitude",
|
||||
"logo": "Logo",
|
||||
"longitude": "Longitude:",
|
||||
"math": {
|
||||
"equals": "=",
|
||||
@ -155,6 +159,8 @@
|
||||
"instances": "Instances",
|
||||
"logout": "Logout",
|
||||
"my-account": "My Account",
|
||||
"projectBranding": "Project Branding",
|
||||
"projectBrandingTooltip": "Use your own logo in the Gradido login and add new users to a Humhub space",
|
||||
"statistic": "Statistic",
|
||||
"user_search": "User search"
|
||||
},
|
||||
@ -199,9 +205,16 @@
|
||||
"yes": "Yes,undelete user"
|
||||
}
|
||||
},
|
||||
"projectBranding": {
|
||||
"addTooltip": "Add new project branding entry",
|
||||
"title": "Project Branding",
|
||||
"newUserToSpace": "Add user?",
|
||||
"newUserToSpaceTooltip": "The hours should contain a maximum of two decimal places"
|
||||
},
|
||||
"redeemed": "redeemed",
|
||||
"removeNotSelf": "As an admin/moderator, you cannot delete yourself.",
|
||||
"save": "Speichern",
|
||||
"reset": "Reset",
|
||||
"save": "Save",
|
||||
"statistic": {
|
||||
"activeUsers": "Active members",
|
||||
"count": "Count",
|
||||
|
||||
119
admin/src/pages/ProjectBranding.vue
Normal file
119
admin/src/pages/ProjectBranding.vue
Normal file
@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<div class="project-branding">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<span class="h2">{{ $t('projectBranding.title') }}</span>
|
||||
<div>
|
||||
<BButton
|
||||
variant="primary"
|
||||
data-test="project-branding-add-btn"
|
||||
font-scale="2"
|
||||
class="me-3"
|
||||
:title="$t('projectBranding.addTooltip')"
|
||||
:disabled="isAddButtonDisabled"
|
||||
@click="createEntry"
|
||||
>
|
||||
<IBiPlus />
|
||||
</BButton>
|
||||
<BButton
|
||||
:animation="animation"
|
||||
data-test="project-branding-refresh-btn"
|
||||
font-scale="2"
|
||||
@click="refetch"
|
||||
>
|
||||
<IBiArrowClockwise />
|
||||
</BButton>
|
||||
</div>
|
||||
</div>
|
||||
<BListGroup>
|
||||
<BRow>
|
||||
<BCol cols="3" class="ms-1">{{ $t('name') }}</BCol>
|
||||
<BCol cols="2">{{ $t('alias') }}</BCol>
|
||||
<BCol cols="2" :title="$t('projectBranding.newUserToSpaceTooltip')">
|
||||
{{ $t('projectBranding.newUserToSpace') }}
|
||||
</BCol>
|
||||
<BCol cols="3">{{ $t('logo') }}</BCol>
|
||||
<BCol cols="1">{{ $t('actions') }}</BCol>
|
||||
</BRow>
|
||||
<BListGroupItem v-for="item in projectBrandings" :key="item.id">
|
||||
<project-branding-item
|
||||
:item="item"
|
||||
@update:item="handleUpdateItem"
|
||||
@deleted:item="handleDeleteItem"
|
||||
/>
|
||||
</BListGroupItem>
|
||||
</BListGroup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, watch, ref } from 'vue'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
const { toastError } = useAppToast()
|
||||
|
||||
const { result, loading, refetch, error } = useQuery(
|
||||
gql`
|
||||
query {
|
||||
projectBrandings {
|
||||
id
|
||||
name
|
||||
alias
|
||||
description
|
||||
spaceId
|
||||
newUserToSpace
|
||||
logoUrl
|
||||
}
|
||||
}
|
||||
`,
|
||||
null,
|
||||
{
|
||||
fetchPolicy: 'network-only',
|
||||
},
|
||||
)
|
||||
|
||||
const projectBrandings = ref([])
|
||||
|
||||
const isAddButtonDisabled = computed(() => {
|
||||
return projectBrandings.value.some((item) => item.id === undefined)
|
||||
})
|
||||
|
||||
watch(
|
||||
result,
|
||||
() => {
|
||||
projectBrandings.value = result.value?.projectBrandings || []
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
function createEntry() {
|
||||
projectBrandings.value.push({
|
||||
id: undefined,
|
||||
name: '',
|
||||
alias: '',
|
||||
description: undefined,
|
||||
spaceId: undefined,
|
||||
newUserToSpace: false,
|
||||
logoUrl: undefined,
|
||||
})
|
||||
}
|
||||
|
||||
function handleUpdateItem(updatedItem) {
|
||||
const index = projectBrandings.value.findIndex(
|
||||
(item) => item.id === updatedItem.id || item.id === undefined,
|
||||
)
|
||||
projectBrandings.value.splice(index, 1, updatedItem)
|
||||
}
|
||||
|
||||
function handleDeleteItem(id) {
|
||||
const index = projectBrandings.value.findIndex((item) => item.id === id)
|
||||
projectBrandings.value.splice(index, 1)
|
||||
}
|
||||
|
||||
watch(error, () => {
|
||||
if (error.value) toastError(error.value.message)
|
||||
})
|
||||
|
||||
const animation = computed(() => (loading.value ? 'spin' : ''))
|
||||
</script>
|
||||
@ -36,6 +36,11 @@ const routes = [
|
||||
name: 'federation',
|
||||
component: () => import('@/pages/FederationVisualize.vue'),
|
||||
},
|
||||
{
|
||||
path: '/projectBranding',
|
||||
name: 'projectBranding',
|
||||
component: () => import('@/pages/ProjectBranding.vue'),
|
||||
},
|
||||
{
|
||||
path: '/:catchAll(.*)',
|
||||
name: 'NotFound',
|
||||
|
||||
14
admin/src/validationSchemas.js
Normal file
14
admin/src/validationSchemas.js
Normal file
@ -0,0 +1,14 @@
|
||||
// TODO: only needed for grace period, before all inputs updated for using veeValidate + yup
|
||||
export const isLanguageKey = (str) =>
|
||||
str.match(/^(?!\.)[a-z][a-zA-Z0-9-]*([.][a-z][a-zA-Z0-9-]*)*(?<!\.)$/)
|
||||
|
||||
export const translateYupErrorString = (error, t) => {
|
||||
const type = typeof error
|
||||
if (type === 'object') {
|
||||
return t(error.key, error.values)
|
||||
} else if (type === 'string' && error.length > 0 && isLanguageKey(error)) {
|
||||
return t(error)
|
||||
} else {
|
||||
return error
|
||||
}
|
||||
}
|
||||
@ -5657,6 +5657,11 @@ prop-types@^15.7.2:
|
||||
object-assign "^4.1.1"
|
||||
react-is "^16.13.1"
|
||||
|
||||
property-expr@^2.0.5:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.6.tgz#f77bc00d5928a6c748414ad12882e83f24aec1e8"
|
||||
integrity sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==
|
||||
|
||||
proto-list@~1.2.1:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
|
||||
@ -6484,6 +6489,11 @@ throttle-debounce@^5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-5.0.2.tgz#ec5549d84e053f043c9fd0f2a6dd892ff84456b1"
|
||||
integrity sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==
|
||||
|
||||
tiny-case@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tiny-case/-/tiny-case-1.0.3.tgz#d980d66bc72b5d5a9ca86fb7c9ffdb9c898ddd03"
|
||||
integrity sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==
|
||||
|
||||
tinybench@^2.9.0:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b"
|
||||
@ -6543,6 +6553,11 @@ toidentifier@1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
|
||||
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
|
||||
|
||||
toposort@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330"
|
||||
integrity sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==
|
||||
|
||||
tough-cookie@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-5.0.0.tgz#6b6518e2b5c070cf742d872ee0f4f92d69eac1af"
|
||||
@ -6608,6 +6623,11 @@ type-fest@^0.20.2:
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
|
||||
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
|
||||
|
||||
type-fest@^2.19.0:
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b"
|
||||
integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==
|
||||
|
||||
type-is@~1.6.18:
|
||||
version "1.6.18"
|
||||
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
|
||||
@ -7145,6 +7165,16 @@ yocto-queue@^0.1.0:
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||
|
||||
yup@^1.6.1:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/yup/-/yup-1.6.1.tgz#8defcff9daaf9feac178029c0e13b616563ada4b"
|
||||
integrity sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA==
|
||||
dependencies:
|
||||
property-expr "^2.0.5"
|
||||
tiny-case "^1.0.3"
|
||||
toposort "^2.0.2"
|
||||
type-fest "^2.19.0"
|
||||
|
||||
zen-observable-ts@^0.8.21:
|
||||
version "0.8.21"
|
||||
resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.21.tgz#85d0031fbbde1eba3cd07d3ba90da241215f421d"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { ProjectBranding as DbProjectBranding } from '@entity/ProjectBranding'
|
||||
import { Resolver, Query, Mutation, Arg, Int, Authorized } from 'type-graphql'
|
||||
import { Resolver, Query, Mutation, Arg, Int, Authorized, ID } from 'type-graphql'
|
||||
|
||||
import { ProjectBrandingInput } from '@input/ProjectBrandingInput'
|
||||
import { ProjectBranding } from '@model/ProjectBranding'
|
||||
@ -44,13 +44,13 @@ export class ProjectBrandingResolver {
|
||||
@Mutation(() => ProjectBranding, { nullable: true })
|
||||
@Authorized([RIGHTS.PROJECT_BRANDING_MUTATE])
|
||||
async upsertProjectBranding(
|
||||
@Arg('data') data: ProjectBrandingInput,
|
||||
@Arg('input') input: ProjectBrandingInput,
|
||||
): Promise<ProjectBranding | null> {
|
||||
const projectBranding = data.id
|
||||
? await DbProjectBranding.findOneOrFail({ where: { id: data.id } })
|
||||
const projectBranding = input.id
|
||||
? await DbProjectBranding.findOneOrFail({ where: { id: input.id } })
|
||||
: new DbProjectBranding()
|
||||
|
||||
Object.assign(projectBranding, data)
|
||||
Object.assign(projectBranding, input)
|
||||
await projectBranding.save()
|
||||
|
||||
return new ProjectBranding(projectBranding)
|
||||
@ -58,7 +58,7 @@ export class ProjectBrandingResolver {
|
||||
|
||||
@Mutation(() => Boolean)
|
||||
@Authorized([RIGHTS.PROJECT_BRANDING_MUTATE])
|
||||
async deleteProjectBranding(@Arg('id', () => Int) id: number): Promise<boolean> {
|
||||
async deleteProjectBranding(@Arg('id', () => ID) id: number): Promise<boolean> {
|
||||
try {
|
||||
await DbProjectBranding.delete({ id })
|
||||
return true
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user