mirror of
https://github.com/abhinavxd/libredesk.git
synced 2025-10-23 05:11:57 +00:00
feat: translate macros
This commit is contained in:
@@ -77,7 +77,7 @@
|
||||
</Avatar>
|
||||
<span>{{ selected.label }}</span>
|
||||
</div>
|
||||
<span v-else>Select user</span>
|
||||
<span v-else>{{ $t('admin.macro.visibility.selectUser') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="action.type === 'assign_team'">
|
||||
@@ -86,13 +86,15 @@
|
||||
{{ selected.emoji }}
|
||||
<span>{{ selected.label }}</span>
|
||||
</span>
|
||||
<span v-else>Select team</span>
|
||||
<span v-else>
|
||||
{{ $t('admin.macro.visibility.selectTeam') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="selected">
|
||||
{{ selected.label }}
|
||||
</div>
|
||||
<div v-else>Select</div>
|
||||
<div v-else>{{ $t('form.field.select') }}</div>
|
||||
</template>
|
||||
</ComboBox>
|
||||
</div>
|
||||
@@ -131,7 +133,7 @@ import { SelectTag } from '@/components/ui/select'
|
||||
import { useTagStore } from '@/stores/tag'
|
||||
import ComboBox from '@/components/ui/combobox/ComboBox.vue'
|
||||
|
||||
const model = defineModel("actions", {
|
||||
const model = defineModel('actions', {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => []
|
||||
|
@@ -3,9 +3,9 @@
|
||||
<form @submit="onSubmit" class="space-y-6 w-full" :class="{ 'opacity-50': formLoading }">
|
||||
<FormField v-slot="{ componentField }" name="name">
|
||||
<FormItem>
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormLabel>{{ t('form.field.name') }} </FormLabel>
|
||||
<FormControl>
|
||||
<Input type="text" placeholder="Macro name" v-bind="componentField" />
|
||||
<Input type="text" placeholder="" v-bind="componentField" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -13,13 +13,13 @@
|
||||
|
||||
<FormField v-slot="{ componentField }" name="message_content">
|
||||
<FormItem>
|
||||
<FormLabel>Response to be sent when macro is used (optional)</FormLabel>
|
||||
<FormLabel>{{ t('admin.macro.message_content') }}</FormLabel>
|
||||
<FormControl>
|
||||
<div class="box p-2 h-96 min-h-96">
|
||||
<Editor
|
||||
v-model:htmlContent="componentField.modelValue"
|
||||
@update:htmlContent="(value) => componentField.onChange(value)"
|
||||
:placeholder="'Shift + Enter to add new line'"
|
||||
:placeholder="t('admin.macro.message_content.placeholder')"
|
||||
/>
|
||||
</div>
|
||||
</FormControl>
|
||||
@@ -29,9 +29,13 @@
|
||||
|
||||
<FormField v-slot="{ componentField }" name="actions">
|
||||
<FormItem>
|
||||
<FormLabel> Actions (optional)</FormLabel>
|
||||
<FormLabel> {{ t('admin.macro.actions') }}</FormLabel>
|
||||
<FormControl>
|
||||
<ActionBuilder v-model:actions="componentField.modelValue" :config="actionConfig" @update:actions="(value) => componentField.onChange(value)" />
|
||||
<ActionBuilder
|
||||
v-model:actions="componentField.modelValue"
|
||||
:config="actionConfig"
|
||||
@update:actions="(value) => componentField.onChange(value)"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -39,7 +43,7 @@
|
||||
|
||||
<FormField v-slot="{ componentField }" name="visibility">
|
||||
<FormItem>
|
||||
<FormLabel>Visibility</FormLabel>
|
||||
<FormLabel>{{ t('admin.macro.visibility') }}</FormLabel>
|
||||
<FormControl>
|
||||
<Select v-bind="componentField">
|
||||
<SelectTrigger>
|
||||
@@ -47,9 +51,9 @@
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="all">All</SelectItem>
|
||||
<SelectItem value="team">Team</SelectItem>
|
||||
<SelectItem value="user">User</SelectItem>
|
||||
<SelectItem value="all">{{ t('admin.macro.visibility.all') }}</SelectItem>
|
||||
<SelectItem value="team">{{ t('admin.macro.visibility.team') }}</SelectItem>
|
||||
<SelectItem value="user">{{ t('admin.macro.visibility.user') }}</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
@@ -60,9 +64,13 @@
|
||||
|
||||
<FormField v-if="form.values.visibility === 'team'" v-slot="{ componentField }" name="team_id">
|
||||
<FormItem>
|
||||
<FormLabel>Team</FormLabel>
|
||||
<FormLabel>{{ t('admin.macro.visibility.user') }}</FormLabel>
|
||||
<FormControl>
|
||||
<ComboBox v-bind="componentField" :items="tStore.options" placeholder="Select team">
|
||||
<ComboBox
|
||||
v-bind="componentField"
|
||||
:items="tStore.options"
|
||||
:placeholder="t('admin.macro.visibility.selectTeam')"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
<div class="flex items-center gap-2 ml-2">
|
||||
<span>{{ item.emoji }}</span>
|
||||
@@ -75,7 +83,7 @@
|
||||
{{ selected.emoji }}
|
||||
<span>{{ selected.label }}</span>
|
||||
</span>
|
||||
<span v-else>Select team</span>
|
||||
<span v-else>{{ t('admin.macro.visibility.selectTeam') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</ComboBox>
|
||||
@@ -86,9 +94,13 @@
|
||||
|
||||
<FormField v-if="form.values.visibility === 'user'" v-slot="{ componentField }" name="user_id">
|
||||
<FormItem>
|
||||
<FormLabel>User</FormLabel>
|
||||
<FormLabel>{{ t('admin.macro.visibility.user') }}</FormLabel>
|
||||
<FormControl>
|
||||
<ComboBox v-bind="componentField" :items="uStore.options" placeholder="Select user">
|
||||
<ComboBox
|
||||
v-bind="componentField"
|
||||
:items="uStore.options"
|
||||
:placeholder="t('admin.macro.visibility.selectUser')"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
<div class="flex items-center gap-2 ml-2">
|
||||
<Avatar class="w-7 h-7">
|
||||
@@ -107,7 +119,7 @@
|
||||
</Avatar>
|
||||
<span>{{ selected.label }}</span>
|
||||
</div>
|
||||
<span v-else>Select user</span>
|
||||
<span v-else>{{ t('admin.macro.visibility.selectUser') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</ComboBox>
|
||||
@@ -120,7 +132,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import { useForm } from 'vee-validate'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { Button } from '@/components/ui/button'
|
||||
@@ -133,7 +145,7 @@ import { useConversationFilters } from '@/composables/useConversationFilters'
|
||||
import { useUsersStore } from '@/stores/users'
|
||||
import { useTeamStore } from '@/stores/team'
|
||||
import { getTextFromHTML } from '@/utils/strings.js'
|
||||
import { formSchema } from './formSchema.js'
|
||||
import { createFormSchema } from './formSchema.js'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -143,9 +155,11 @@ import {
|
||||
SelectValue
|
||||
} from '@/components/ui/select'
|
||||
import ComboBox from '@/components/ui/combobox/ComboBox.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import Editor from '@/features/conversation/ConversationTextEditor.vue'
|
||||
|
||||
const { macroActions } = useConversationFilters()
|
||||
const { t } = useI18n()
|
||||
const formLoading = ref(false)
|
||||
const uStore = useUsersStore()
|
||||
const tStore = useTeamStore()
|
||||
@@ -160,7 +174,7 @@ const props = defineProps({
|
||||
},
|
||||
submitLabel: {
|
||||
type: String,
|
||||
default: 'Submit'
|
||||
default: ''
|
||||
},
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
@@ -168,15 +182,21 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
const submitLabel = computed(() => {
|
||||
return (
|
||||
props.submitLabel ||
|
||||
(props.initialValues.id ? t('globals.buttons.update') : t('globals.buttons.create'))
|
||||
)
|
||||
})
|
||||
const form = useForm({
|
||||
validationSchema: toTypedSchema(formSchema)
|
||||
validationSchema: toTypedSchema(createFormSchema(t))
|
||||
})
|
||||
|
||||
const actionConfig = ref({
|
||||
actions: macroActions,
|
||||
typePlaceholder: 'Select action type',
|
||||
valuePlaceholder: 'Select value',
|
||||
addButtonText: 'Add new action'
|
||||
typePlaceholder: t('admin.macro.visibility.selectActionType'),
|
||||
valuePlaceholder: t('admin.macro.visibility.selectValue'),
|
||||
addButtonText: t('admin.macro.visibility.addNewAction')
|
||||
})
|
||||
|
||||
const onSubmit = form.handleSubmit(async (values) => {
|
||||
|
@@ -2,11 +2,11 @@ import { h } from 'vue'
|
||||
import dropdown from './dataTableDropdown.vue'
|
||||
import { format } from 'date-fns'
|
||||
|
||||
export const columns = [
|
||||
export const createColumns = (t) => [
|
||||
{
|
||||
accessorKey: 'name',
|
||||
header: function () {
|
||||
return h('div', { class: 'text-center' }, 'Name')
|
||||
return h('div', { class: 'text-center' }, t('form.field.name'))
|
||||
},
|
||||
cell: function ({ row }) {
|
||||
return h('div', { class: 'text-center font-medium' }, row.getValue('name'))
|
||||
@@ -15,7 +15,7 @@ export const columns = [
|
||||
{
|
||||
accessorKey: 'visibility',
|
||||
header: function () {
|
||||
return h('div', { class: 'text-center' }, 'Visibility')
|
||||
return h('div', { class: 'text-center' }, t('admin.macro.visibility'))
|
||||
},
|
||||
cell: function ({ row }) {
|
||||
return h('div', { class: 'text-center' }, row.getValue('visibility'))
|
||||
@@ -24,7 +24,7 @@ export const columns = [
|
||||
{
|
||||
accessorKey: 'usage_count',
|
||||
header: function () {
|
||||
return h('div', { class: 'text-center' }, 'Usage')
|
||||
return h('div', { class: 'text-center' }, t('form.field.usage'))
|
||||
},
|
||||
cell: function ({ row }) {
|
||||
return h('div', { class: 'text-center' }, row.getValue('usage_count'))
|
||||
@@ -33,7 +33,7 @@ export const columns = [
|
||||
{
|
||||
accessorKey: 'created_at',
|
||||
header: function () {
|
||||
return h('div', { class: 'text-center' }, 'Created at')
|
||||
return h('div', { class: 'text-center' }, t('form.field.createdAt'))
|
||||
},
|
||||
cell: function ({ row }) {
|
||||
return h('div', { class: 'text-center' }, format(row.getValue('created_at'), 'PPpp'))
|
||||
@@ -42,7 +42,7 @@ export const columns = [
|
||||
{
|
||||
accessorKey: 'updated_at',
|
||||
header: function () {
|
||||
return h('div', { class: 'text-center' }, 'Updated at')
|
||||
return h('div', { class: 'text-center' }, t('form.field.updatedAt'))
|
||||
},
|
||||
cell: function ({ row }) {
|
||||
return h('div', { class: 'text-center' }, format(row.getValue('updated_at'), 'PPpp'))
|
||||
|
@@ -2,27 +2,31 @@
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger as-child>
|
||||
<Button variant="ghost" class="w-8 h-8 p-0">
|
||||
<span class="sr-only">Open menu</span>
|
||||
<span class="sr-only"></span>
|
||||
<MoreHorizontal class="w-4 h-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem @click="editMacro">Edit</DropdownMenuItem>
|
||||
<DropdownMenuItem @click="() => (isDeleteOpen = true)">Delete</DropdownMenuItem>
|
||||
<DropdownMenuItem @click="editMacro">{{ $t('globals.buttons.edit') }}</DropdownMenuItem>
|
||||
<DropdownMenuItem @click="() => (isDeleteOpen = true)">
|
||||
{{ $t('globals.buttons.delete') }}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
<AlertDialog :open="isDeleteOpen" @update:open="isDeleteOpen = $event">
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Delete Macro</AlertDialogTitle>
|
||||
<AlertDialogTitle>{{ $t('admin.macro.delete_confirmation_title') }}</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
Are you sure you want to delete this macro? This action cannot be undone.
|
||||
{{ $t('admin.macro.delete_confirmation') }}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction @click="handleDelete">Delete</AlertDialogAction>
|
||||
<AlertDialogCancel>{{ $t('globals.buttons.cancel') }}</AlertDialogCancel>
|
||||
<AlertDialogAction @click="handleDelete">{{
|
||||
$t('globals.buttons.delete')
|
||||
}}</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
|
@@ -1,17 +1,17 @@
|
||||
import * as z from 'zod'
|
||||
import { getTextFromHTML } from '@/utils/strings.js'
|
||||
|
||||
const actionSchema = z.array(
|
||||
const actionSchema = (t) => z.array(
|
||||
z.object({
|
||||
type: z.string().min(1, 'Action type required'),
|
||||
value: z.array(z.string().min(1, 'Action value required')).min(1, 'Action value required'),
|
||||
type: z.string().min(1, t('admin.macro.actionTypeRequired')),
|
||||
value: z.array(z.string().min(1, t('admin.macro.actionValueRequired'))),
|
||||
})
|
||||
)
|
||||
|
||||
export const formSchema = z.object({
|
||||
name: z.string().min(1, 'Macro name is required'),
|
||||
export const createFormSchema = (t) => z.object({
|
||||
name: z.string().min(1, t('form.error.name.required')),
|
||||
message_content: z.string().optional(),
|
||||
actions: actionSchema.optional().default([]), // Default to empty array if not provided
|
||||
actions: actionSchema(t).optional().default([]),
|
||||
visibility: z.enum(['all', 'team', 'user']),
|
||||
team_id: z.string().nullable().optional(),
|
||||
user_id: z.string().nullable().optional(),
|
||||
@@ -26,7 +26,7 @@ export const formSchema = z.object({
|
||||
return hasMessageContent || hasValidActions
|
||||
},
|
||||
{
|
||||
message: 'Either message content or actions are required',
|
||||
message: t('admin.macro.messageOrActionRequired'),
|
||||
// Field path to highlight
|
||||
path: ['message_content'],
|
||||
}
|
||||
@@ -45,7 +45,7 @@ export const formSchema = z.object({
|
||||
return true
|
||||
},
|
||||
{
|
||||
message: 'team is required when visibility is "team", and user is required when visibility is "user"',
|
||||
message: t('admin.macro.teamOrUserRequired'),
|
||||
// Field path to highlight
|
||||
path: ['visibility'],
|
||||
}
|
||||
|
@@ -2,10 +2,7 @@
|
||||
<div class="mb-5">
|
||||
<CustomBreadcrumb :links="breadcrumbLinks" />
|
||||
</div>
|
||||
<MacroForm
|
||||
:submitForm="onSubmit"
|
||||
:isLoading="formLoading"
|
||||
/>
|
||||
<MacroForm :submitForm="onSubmit" :isLoading="formLoading" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -16,14 +13,16 @@ import { handleHTTPError } from '@/utils/http'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useEmitter } from '@/composables/useEmitter'
|
||||
import { EMITTER_EVENTS } from '@/constants/emitterEvents.js'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import api from '@/api'
|
||||
|
||||
const router = useRouter()
|
||||
const emit = useEmitter()
|
||||
const { t } = useI18n()
|
||||
const formLoading = ref(false)
|
||||
const breadcrumbLinks = [
|
||||
{ path: 'macro-list', label: 'Macros' },
|
||||
{ path: '', label: 'New macro' }
|
||||
{ path: 'macro-list', label: t('admin.macro') },
|
||||
{ path: '', label: t('admin.macro.new') }
|
||||
]
|
||||
|
||||
const onSubmit = (values) => {
|
||||
@@ -35,13 +34,11 @@ const createMacro = async (values) => {
|
||||
formLoading.value = true
|
||||
await api.createMacro(values)
|
||||
emit.emit(EMITTER_EVENTS.SHOW_TOAST, {
|
||||
title: 'Success',
|
||||
description: 'Macro created successfully'
|
||||
description: t('admin.macro.created')
|
||||
})
|
||||
router.push({ name: 'macro-list' })
|
||||
} catch (error) {
|
||||
emit.emit(EMITTER_EVENTS.SHOW_TOAST, {
|
||||
title: 'Error',
|
||||
variant: 'destructive',
|
||||
description: handleHTTPError(error).message
|
||||
})
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<div class="mb-5">
|
||||
<CustomBreadcrumb :links="breadcrumbLinks" />
|
||||
</div>
|
||||
<Spinner v-if="isLoading"></Spinner>
|
||||
<Spinner v-if="isLoading"/>
|
||||
<MacroForm :initialValues="macro" :submitForm="submitForm" :isLoading="formLoading" v-else />
|
||||
</template>
|
||||
|
||||
@@ -14,16 +14,18 @@ import { useEmitter } from '@/composables/useEmitter'
|
||||
import { handleHTTPError } from '@/utils/http'
|
||||
import MacroForm from '@/features/admin/macros/MacroForm.vue'
|
||||
import { CustomBreadcrumb } from '@/components/ui/breadcrumb'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { Spinner } from '@/components/ui/spinner'
|
||||
|
||||
const macro = ref({})
|
||||
const { t } = useI18n()
|
||||
const isLoading = ref(false)
|
||||
const formLoading = ref(false)
|
||||
const emitter = useEmitter()
|
||||
|
||||
const breadcrumbLinks = [
|
||||
{ path: 'macro-list', label: 'Macros' },
|
||||
{ path: '', label: 'Edit macro' }
|
||||
{ path: 'macro-list', label: t('admin.macro') },
|
||||
{ path: '', label: t('admin.macro.edit') }
|
||||
]
|
||||
|
||||
const submitForm = (values) => {
|
||||
@@ -35,12 +37,10 @@ const updateMacro = async (payload) => {
|
||||
formLoading.value = true
|
||||
await api.updateMacro(macro.value.id, payload)
|
||||
emitter.emit(EMITTER_EVENTS.SHOW_TOAST, {
|
||||
title: 'Success',
|
||||
description: 'Macro updated successfully'
|
||||
description: t('admin.macro.updated')
|
||||
})
|
||||
} catch (error) {
|
||||
emitter.emit(EMITTER_EVENTS.SHOW_TOAST, {
|
||||
title: 'Could not update macro',
|
||||
variant: 'destructive',
|
||||
description: handleHTTPError(error).message
|
||||
})
|
||||
@@ -56,7 +56,6 @@ onMounted(async () => {
|
||||
macro.value = resp.data.data
|
||||
} catch (error) {
|
||||
emitter.emit(EMITTER_EVENTS.SHOW_TOAST, {
|
||||
title: 'Error',
|
||||
variant: 'destructive',
|
||||
description: handleHTTPError(error).message
|
||||
})
|
||||
|
@@ -3,11 +3,11 @@
|
||||
<div :class="{ 'transition-opacity duration-300 opacity-50': formLoading }">
|
||||
<div class="flex justify-end mb-5">
|
||||
<router-link :to="{ name: 'new-macro' }">
|
||||
<Button> New macro </Button>
|
||||
<Button> {{ $t('admin.macro.new') }} </Button>
|
||||
</router-link>
|
||||
</div>
|
||||
<div>
|
||||
<DataTable :columns="columns" :data="macros" />
|
||||
<DataTable :columns="createColumns(t)" :data="macros" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -15,14 +15,16 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import DataTable from '@/components/datatable/DataTable.vue'
|
||||
import { columns } from '../../../features/admin/macros/dataTableColumns.js'
|
||||
import { createColumns } from '@/features/admin/macros/dataTableColumns.js'
|
||||
import { Spinner } from '@/components/ui/spinner'
|
||||
import { useEmitter } from '@/composables/useEmitter'
|
||||
import { EMITTER_EVENTS } from '@/constants/emitterEvents.js'
|
||||
import { handleHTTPError } from '@/utils/http'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import api from '@/api'
|
||||
|
||||
const { t } = useI18n()
|
||||
const formLoading = ref(false)
|
||||
const macros = ref([])
|
||||
const emit = useEmitter()
|
||||
@@ -47,7 +49,6 @@ const getMacros = async () => {
|
||||
macros.value = resp.data.data
|
||||
} catch (error) {
|
||||
emit.emit(EMITTER_EVENTS.SHOW_TOAST, {
|
||||
title: 'Error',
|
||||
variant: 'destructive',
|
||||
description: handleHTTPError(error).message
|
||||
})
|
||||
|
26
i18n/en.json
26
i18n/en.json
@@ -197,10 +197,13 @@
|
||||
"navigation.edit": "Edit",
|
||||
"navigation.delete": "Delete",
|
||||
"form.field.name": "Name",
|
||||
"form.field.select": "Select",
|
||||
"form.field.date": "Date",
|
||||
"form.field.description": "Description",
|
||||
"form.field.email": "Email",
|
||||
"form.field.password": "Password",
|
||||
"form.field.visibility": "Visibility",
|
||||
"form.field.usage": "Usage",
|
||||
"form.field.createdAt": "Created At",
|
||||
"form.field.updatedAt": "Updated At",
|
||||
"form.field.pick_a_date": "Pick a date",
|
||||
@@ -291,6 +294,29 @@
|
||||
"admin.conversation_tags.name.valid": "Tag name should at least 3 characters",
|
||||
"admin.conversation_tags.delete_confirmation_title": "Are you absolutely sure?",
|
||||
"admin.conversation_tags.delete_confirmation": "This action cannot be undone. This will permanently delete this tag, and remove it from all conversations.",
|
||||
"admin.macro.message_content": "Response to be sent when macro is used (optional)",
|
||||
"admin.macro.message_content.placeholder": "Shift + Enter to add a new line",
|
||||
"admin.macro.actions": "Actions (optional)",
|
||||
"admin.macro.visibility": "Visibility",
|
||||
"admin.macro.visibility.all": "All users",
|
||||
"admin.macro.visibility.team": "Team",
|
||||
"admin.macro.visibility.selectTeam": "Select team",
|
||||
"admin.macro.visibility.selectUser": "Select user",
|
||||
"admin.macro.visibility.user": "User",
|
||||
"admin.macro.visibility.selectActionType": "Select action type",
|
||||
"admin.macro.visibility.selectValue": "Select value",
|
||||
"admin.macro.visibility.addNewAction": "Add new action",
|
||||
"admin.macro.messageOrActionRequired": "Either message content or actions are required",
|
||||
"admin.macro.actionTypeRequired": "Action type is required",
|
||||
"admin.macro.actionValueRequired": "Action value is required",
|
||||
"admin.macro.teamOrUserRequired": "team is required when visibility is `team` & a user is required when visibility is `user`",
|
||||
"admin.macro.delete_confirmation_title": "Are you absolutely sure?",
|
||||
"admin.macro.delete_confirmation": "This action cannot be undone. This will permanently delete this macro.",
|
||||
"admin.macro.created": "Macro created successfully",
|
||||
"admin.macro": "Macros",
|
||||
"admin.macro.new": "New Macro",
|
||||
"admin.macro.edit": "Edit Macro",
|
||||
"admin.macro.updated": "Macro updated successfully",
|
||||
"globals.buttons.save": "Save",
|
||||
"globals.buttons.save_changes": "Save changes",
|
||||
"globals.buttons.cancel": "Cancel",
|
||||
|
Reference in New Issue
Block a user