mirror of
https://github.com/abhinavxd/libredesk.git
synced 2025-10-23 05:11:57 +00:00
feat: regex hint
This commit is contained in:
@@ -87,6 +87,16 @@ export const adminNavItems = [
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
titleKey: 'navigation.customAttributes',
|
||||
children: [
|
||||
{
|
||||
titleKey: 'navigation.customAttributes',
|
||||
href: '/admin/custom-attributes',
|
||||
permission: 'custom_attributes:manage'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
titleKey: 'navigation.notifications',
|
||||
children: [
|
||||
@@ -117,16 +127,6 @@ export const adminNavItems = [
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
titleKey: 'globals.terms.customAttribute',
|
||||
children: [
|
||||
{
|
||||
titleKey: 'globals.terms.customAttribute',
|
||||
href: '/admin/custom-attributes',
|
||||
permission: 'custom_attributes:manage'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
export const accountNavItems = [
|
||||
|
@@ -119,6 +119,19 @@
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField name="regex_hint" v-slot="{ componentField }">
|
||||
<FormItem v-show="form.values.data_type === 'text'">
|
||||
<FormLabel> {{ $t('form.field.regexHint') }} ({{ $t('form.field.optional') }}) </FormLabel>
|
||||
<FormControl>
|
||||
<Input type="text" v-bind="componentField" />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
{{ $t('admin.customAttributes.regexHint.description') }}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<!-- Form submit button slot -->
|
||||
<slot name="footer"></slot>
|
||||
</form>
|
||||
|
@@ -61,6 +61,7 @@ export const createFormSchema = (t) => z.object({
|
||||
required_error: t('globals.messages.required'),
|
||||
}),
|
||||
regex: z.string().optional(),
|
||||
regex_hint: z.string().optional(),
|
||||
values: z.array(z.string())
|
||||
.default([])
|
||||
})
|
||||
|
@@ -143,6 +143,7 @@
|
||||
</AccordionTrigger>
|
||||
<AccordionContent class="p-4">
|
||||
<CustomAttributes
|
||||
:loading="conversationStore.current.loading"
|
||||
:attributes="customAttributeStore.contactAttributeOptions"
|
||||
:customAttributes="conversationStore.current?.contact?.custom_attributes || {}"
|
||||
@update:setattributes="updateContactCustomAttributes"
|
||||
|
@@ -9,7 +9,7 @@
|
||||
</p>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Info class="text-muted-foreground" size="16" />
|
||||
<Info class="text-muted-foreground" size="14" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
{{ attribute.description }}
|
||||
@@ -36,7 +36,7 @@
|
||||
</p>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Info class="text-muted-foreground" size="16" />
|
||||
<Info class="text-muted-foreground" size="14" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
{{ attribute.description }}
|
||||
@@ -177,13 +177,13 @@ const getValidationSchema = (attribute) => {
|
||||
switch (attribute.data_type) {
|
||||
case 'text': {
|
||||
let schema = z.string().min(1, t('globals.messages.required'))
|
||||
// If regex is provided and valid, add it to the schema validation.
|
||||
// If regex is provided and valid, add it to the schema validation along with the hint
|
||||
if (attribute.regex) {
|
||||
try {
|
||||
console.log('Creating regex:', attribute.regex)
|
||||
const regex = new RegExp(attribute.regex)
|
||||
schema = schema.regex(regex, {
|
||||
message: t('globals.messages.invalid', { name: t('form.field.value').toLowerCase() })
|
||||
message: attribute.regex_hint
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('Error creating regex:', err)
|
||||
|
@@ -246,8 +246,10 @@
|
||||
"navigation.views": "Views",
|
||||
"navigation.reassignReplies": "Reassign replies",
|
||||
"navigation.allContacts": "All Contacts",
|
||||
"navigation.customAttributes": "Custom Attributes",
|
||||
"form.field.name": "Name",
|
||||
"form.field.regex": "Regex",
|
||||
"form.field.regexHint": "Regex hint",
|
||||
"form.field.key": "Key",
|
||||
"form.field.type": "Type",
|
||||
"form.field.listValues": "List values",
|
||||
@@ -607,5 +609,6 @@
|
||||
"contact.unblockConfirm": "Are you sure you want to unblock this contact? They will be able to interact with you again.",
|
||||
"contact.alreadyExistsWithEmail": "Another contact with same email already exists",
|
||||
"admin.customAttributes.deleteConfirmation": "This action cannot be undone. This will permanently delete this custom attribute.",
|
||||
"admin.customAttributes.regex.description": "Regex to validate the value of this custom attribute. Leave empty to skip validation."
|
||||
"admin.customAttributes.regex.description": "Regex to validate the value of this custom attribute. Leave empty to skip validation.",
|
||||
"admin.customAttributes.regexHint.description": "Regex pattern hint."
|
||||
}
|
@@ -80,7 +80,7 @@ func (m *Manager) GetAll(appliesTo string) ([]models.CustomAttribute, error) {
|
||||
|
||||
// Create creates a new custom attribute.
|
||||
func (m *Manager) Create(attr models.CustomAttribute) error {
|
||||
if _, err := m.q.InsertCustomAttribute.Exec(attr.AppliesTo, attr.Name, attr.Description, attr.Key, pq.Array(attr.Values), attr.DataType, attr.Regex); err != nil {
|
||||
if _, err := m.q.InsertCustomAttribute.Exec(attr.AppliesTo, attr.Name, attr.Description, attr.Key, pq.Array(attr.Values), attr.DataType, attr.Regex, attr.RegexHint); err != nil {
|
||||
if dbutil.IsUniqueViolationError(err) {
|
||||
return envelope.NewError(envelope.InputError, m.i18n.Ts("globals.messages.errorAlreadyExists", "name", m.i18n.P("globals.terms.customAttribute")), nil)
|
||||
}
|
||||
@@ -92,7 +92,7 @@ func (m *Manager) Create(attr models.CustomAttribute) error {
|
||||
|
||||
// Update updates a custom attribute by ID.
|
||||
func (m *Manager) Update(id int, attr models.CustomAttribute) error {
|
||||
if _, err := m.q.UpdateCustomAttribute.Exec(id, attr.AppliesTo, attr.Name, attr.Description, pq.Array(attr.Values), attr.DataType, attr.Regex); err != nil {
|
||||
if _, err := m.q.UpdateCustomAttribute.Exec(id, attr.AppliesTo, attr.Name, attr.Description, pq.Array(attr.Values), attr.DataType, attr.Regex, attr.RegexHint); err != nil {
|
||||
m.lo.Error("error updating custom attribute", "error", err)
|
||||
return envelope.NewError(envelope.GeneralError, m.i18n.Ts("globals.messages.errorUpdating", "name", "{globals.terms.customAttribute}"), nil)
|
||||
}
|
||||
|
@@ -17,4 +17,5 @@ type CustomAttribute struct {
|
||||
Values pq.StringArray `db:"values" json:"values"`
|
||||
DataType string `db:"data_type" json:"data_type"`
|
||||
Regex string `db:"regex" json:"regex"`
|
||||
RegexHint string `db:"regex_hint" json:"regex_hint"`
|
||||
}
|
||||
|
@@ -9,7 +9,8 @@ SELECT
|
||||
key,
|
||||
values,
|
||||
data_type,
|
||||
regex
|
||||
regex,
|
||||
regex_hint
|
||||
FROM
|
||||
custom_attribute_definitions
|
||||
WHERE
|
||||
@@ -30,7 +31,8 @@ SELECT
|
||||
key,
|
||||
values,
|
||||
data_type,
|
||||
regex
|
||||
regex,
|
||||
regex_hint
|
||||
FROM
|
||||
custom_attribute_definitions
|
||||
WHERE
|
||||
@@ -38,9 +40,9 @@ WHERE
|
||||
|
||||
-- name: insert-custom-attribute
|
||||
INSERT INTO
|
||||
custom_attribute_definitions (applies_to, name, description, key, values, data_type, regex)
|
||||
custom_attribute_definitions (applies_to, name, description, key, values, data_type, regex, regex_hint)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5, $6, $7)
|
||||
($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
|
||||
-- name: delete-custom-attribute
|
||||
DELETE FROM
|
||||
@@ -58,6 +60,7 @@ SET
|
||||
values = $5,
|
||||
data_type = $6,
|
||||
regex = $7,
|
||||
regex_hint = $8,
|
||||
updated_at = NOW()
|
||||
WHERE
|
||||
id = $1;
|
@@ -98,11 +98,17 @@ func V0_6_0(db *sqlx.DB, fs stuffbin.FileSystem, ko *koanf.Koanf) error {
|
||||
values TEXT[] DEFAULT '{}'::TEXT[] NOT NULL,
|
||||
data_type TEXT NOT NULL,
|
||||
regex TEXT NULL,
|
||||
regex_hint TEXT NULL,
|
||||
CONSTRAINT constraint_custom_attribute_definitions_on_name CHECK (length("name") <= 140),
|
||||
CONSTRAINT constraint_custom_attribute_definitions_on_description CHECK (length(description) <= 300),
|
||||
CONSTRAINT constraint_custom_attribute_definitions_on_key CHECK (length(key) <= 140),
|
||||
CONSTRAINT constraint_custom_attribute_definitions_on_applies_to CHECK (length(applies_to) <= 50),
|
||||
CONSTRAINT constraint_custom_attribute_definitions_on_data_type CHECK (length(data_type) <= 100),
|
||||
CONSTRAINT constraint_custom_attribute_definitions_on_regex CHECK (length(regex) <= 1000),
|
||||
CONSTRAINT constraint_custom_attribute_definitions_on_regex_hint CHECK (length(regex_hint) <= 1000),
|
||||
CONSTRAINT constraint_custom_attribute_definitions_key_applies_to_unique UNIQUE (key, applies_to)
|
||||
);
|
||||
|
||||
`)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@@ -508,9 +508,14 @@ CREATE TABLE custom_attribute_definitions (
|
||||
values TEXT[] DEFAULT '{}'::TEXT[] NOT NULL,
|
||||
data_type TEXT NOT NULL,
|
||||
regex TEXT NULL,
|
||||
regex_hint TEXT NULL,
|
||||
CONSTRAINT constraint_custom_attribute_definitions_on_name CHECK (length("name") <= 140),
|
||||
CONSTRAINT constraint_custom_attribute_definitions_on_description CHECK (length(description) <= 300),
|
||||
CONSTRAINT constraint_custom_attribute_definitions_on_key CHECK (length(key) <= 140),
|
||||
CONSTRAINT constraint_custom_attribute_definitions_on_applies_to CHECK (length(applies_to) <= 50),
|
||||
CONSTRAINT constraint_custom_attribute_definitions_on_data_type CHECK (length(data_type) <= 100),
|
||||
CONSTRAINT constraint_custom_attribute_definitions_on_regex CHECK (length(regex) <= 1000),
|
||||
CONSTRAINT constraint_custom_attribute_definitions_on_regex_hint CHECK (length(regex_hint) <= 1000),
|
||||
CONSTRAINT constraint_custom_attribute_definitions_key_applies_to_unique UNIQUE (key, applies_to)
|
||||
);
|
||||
|
||||
|
Reference in New Issue
Block a user