Skip to content

Commit 0254bab

Browse files
committed
feat: regex hint
1 parent 91372f5 commit 0254bab

File tree

11 files changed

+54
-21
lines changed

11 files changed

+54
-21
lines changed

frontend/src/constants/navigation.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,16 @@ export const adminNavItems = [
8787
}
8888
]
8989
},
90+
{
91+
titleKey: 'navigation.customAttributes',
92+
children: [
93+
{
94+
titleKey: 'navigation.customAttributes',
95+
href: '/admin/custom-attributes',
96+
permission: 'custom_attributes:manage'
97+
}
98+
]
99+
},
90100
{
91101
titleKey: 'navigation.notifications',
92102
children: [
@@ -117,16 +127,6 @@ export const adminNavItems = [
117127
}
118128
]
119129
},
120-
{
121-
titleKey: 'globals.terms.customAttribute',
122-
children: [
123-
{
124-
titleKey: 'globals.terms.customAttribute',
125-
href: '/admin/custom-attributes',
126-
permission: 'custom_attributes:manage'
127-
}
128-
]
129-
}
130130
]
131131

132132
export const accountNavItems = [

frontend/src/features/admin/custom-attributes/CustomAttributesForm.vue

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,19 @@
119119
</FormItem>
120120
</FormField>
121121

122+
<FormField name="regex_hint" v-slot="{ componentField }">
123+
<FormItem v-show="form.values.data_type === 'text'">
124+
<FormLabel> {{ $t('form.field.regexHint') }} ({{ $t('form.field.optional') }}) </FormLabel>
125+
<FormControl>
126+
<Input type="text" v-bind="componentField" />
127+
</FormControl>
128+
<FormDescription>
129+
{{ $t('admin.customAttributes.regexHint.description') }}
130+
</FormDescription>
131+
<FormMessage />
132+
</FormItem>
133+
</FormField>
134+
122135
<!-- Form submit button slot -->
123136
<slot name="footer"></slot>
124137
</form>

frontend/src/features/admin/custom-attributes/formSchema.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export const createFormSchema = (t) => z.object({
6161
required_error: t('globals.messages.required'),
6262
}),
6363
regex: z.string().optional(),
64+
regex_hint: z.string().optional(),
6465
values: z.array(z.string())
6566
.default([])
6667
})

frontend/src/features/conversation/sidebar/ConversationSideBar.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@
143143
</AccordionTrigger>
144144
<AccordionContent class="p-4">
145145
<CustomAttributes
146+
:loading="conversationStore.current.loading"
146147
:attributes="customAttributeStore.contactAttributeOptions"
147148
:customAttributes="conversationStore.current?.contact?.custom_attributes || {}"
148149
@update:setattributes="updateContactCustomAttributes"

frontend/src/features/conversation/sidebar/CustomAttributes.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
</p>
1010
<Tooltip>
1111
<TooltipTrigger>
12-
<Info class="text-muted-foreground" size="16" />
12+
<Info class="text-muted-foreground" size="14" />
1313
</TooltipTrigger>
1414
<TooltipContent>
1515
{{ attribute.description }}
@@ -36,7 +36,7 @@
3636
</p>
3737
<Tooltip>
3838
<TooltipTrigger>
39-
<Info class="text-muted-foreground" size="16" />
39+
<Info class="text-muted-foreground" size="14" />
4040
</TooltipTrigger>
4141
<TooltipContent>
4242
{{ attribute.description }}
@@ -177,13 +177,13 @@ const getValidationSchema = (attribute) => {
177177
switch (attribute.data_type) {
178178
case 'text': {
179179
let schema = z.string().min(1, t('globals.messages.required'))
180-
// If regex is provided and valid, add it to the schema validation.
180+
// If regex is provided and valid, add it to the schema validation along with the hint
181181
if (attribute.regex) {
182182
try {
183183
console.log('Creating regex:', attribute.regex)
184184
const regex = new RegExp(attribute.regex)
185185
schema = schema.regex(regex, {
186-
message: t('globals.messages.invalid', { name: t('form.field.value').toLowerCase() })
186+
message: attribute.regex_hint
187187
})
188188
} catch (err) {
189189
console.error('Error creating regex:', err)

i18n/en.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,10 @@
246246
"navigation.views": "Views",
247247
"navigation.reassignReplies": "Reassign replies",
248248
"navigation.allContacts": "All Contacts",
249+
"navigation.customAttributes": "Custom Attributes",
249250
"form.field.name": "Name",
250251
"form.field.regex": "Regex",
252+
"form.field.regexHint": "Regex hint",
251253
"form.field.key": "Key",
252254
"form.field.type": "Type",
253255
"form.field.listValues": "List values",
@@ -607,5 +609,6 @@
607609
"contact.unblockConfirm": "Are you sure you want to unblock this contact? They will be able to interact with you again.",
608610
"contact.alreadyExistsWithEmail": "Another contact with same email already exists",
609611
"admin.customAttributes.deleteConfirmation": "This action cannot be undone. This will permanently delete this custom attribute.",
610-
"admin.customAttributes.regex.description": "Regex to validate the value of this custom attribute. Leave empty to skip validation."
612+
"admin.customAttributes.regex.description": "Regex to validate the value of this custom attribute. Leave empty to skip validation.",
613+
"admin.customAttributes.regexHint.description": "Regex pattern hint."
611614
}

internal/custom_attribute/custom_attribute.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func (m *Manager) GetAll(appliesTo string) ([]models.CustomAttribute, error) {
8080

8181
// Create creates a new custom attribute.
8282
func (m *Manager) Create(attr models.CustomAttribute) error {
83-
if _, err := m.q.InsertCustomAttribute.Exec(attr.AppliesTo, attr.Name, attr.Description, attr.Key, pq.Array(attr.Values), attr.DataType, attr.Regex); err != nil {
83+
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 {
8484
if dbutil.IsUniqueViolationError(err) {
8585
return envelope.NewError(envelope.InputError, m.i18n.Ts("globals.messages.errorAlreadyExists", "name", m.i18n.P("globals.terms.customAttribute")), nil)
8686
}
@@ -92,7 +92,7 @@ func (m *Manager) Create(attr models.CustomAttribute) error {
9292

9393
// Update updates a custom attribute by ID.
9494
func (m *Manager) Update(id int, attr models.CustomAttribute) error {
95-
if _, err := m.q.UpdateCustomAttribute.Exec(id, attr.AppliesTo, attr.Name, attr.Description, pq.Array(attr.Values), attr.DataType, attr.Regex); err != nil {
95+
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 {
9696
m.lo.Error("error updating custom attribute", "error", err)
9797
return envelope.NewError(envelope.GeneralError, m.i18n.Ts("globals.messages.errorUpdating", "name", "{globals.terms.customAttribute}"), nil)
9898
}

internal/custom_attribute/models/models.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ type CustomAttribute struct {
1717
Values pq.StringArray `db:"values" json:"values"`
1818
DataType string `db:"data_type" json:"data_type"`
1919
Regex string `db:"regex" json:"regex"`
20+
RegexHint string `db:"regex_hint" json:"regex_hint"`
2021
}

internal/custom_attribute/queries.sql

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ SELECT
99
key,
1010
values,
1111
data_type,
12-
regex
12+
regex,
13+
regex_hint
1314
FROM
1415
custom_attribute_definitions
1516
WHERE
@@ -30,17 +31,18 @@ SELECT
3031
key,
3132
values,
3233
data_type,
33-
regex
34+
regex,
35+
regex_hint
3436
FROM
3537
custom_attribute_definitions
3638
WHERE
3739
id = $1;
3840

3941
-- name: insert-custom-attribute
4042
INSERT INTO
41-
custom_attribute_definitions (applies_to, name, description, key, values, data_type, regex)
43+
custom_attribute_definitions (applies_to, name, description, key, values, data_type, regex, regex_hint)
4244
VALUES
43-
($1, $2, $3, $4, $5, $6, $7)
45+
($1, $2, $3, $4, $5, $6, $7, $8)
4446

4547
-- name: delete-custom-attribute
4648
DELETE FROM
@@ -58,6 +60,7 @@ SET
5860
values = $5,
5961
data_type = $6,
6062
regex = $7,
63+
regex_hint = $8,
6164
updated_at = NOW()
6265
WHERE
6366
id = $1;

internal/migrations/v0.6.0.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,17 @@ func V0_6_0(db *sqlx.DB, fs stuffbin.FileSystem, ko *koanf.Koanf) error {
9898
values TEXT[] DEFAULT '{}'::TEXT[] NOT NULL,
9999
data_type TEXT NOT NULL,
100100
regex TEXT NULL,
101+
regex_hint TEXT NULL,
101102
CONSTRAINT constraint_custom_attribute_definitions_on_name CHECK (length("name") <= 140),
102103
CONSTRAINT constraint_custom_attribute_definitions_on_description CHECK (length(description) <= 300),
103104
CONSTRAINT constraint_custom_attribute_definitions_on_key CHECK (length(key) <= 140),
105+
CONSTRAINT constraint_custom_attribute_definitions_on_applies_to CHECK (length(applies_to) <= 50),
106+
CONSTRAINT constraint_custom_attribute_definitions_on_data_type CHECK (length(data_type) <= 100),
107+
CONSTRAINT constraint_custom_attribute_definitions_on_regex CHECK (length(regex) <= 1000),
108+
CONSTRAINT constraint_custom_attribute_definitions_on_regex_hint CHECK (length(regex_hint) <= 1000),
104109
CONSTRAINT constraint_custom_attribute_definitions_key_applies_to_unique UNIQUE (key, applies_to)
105110
);
111+
106112
`)
107113
if err != nil {
108114
return err

0 commit comments

Comments
 (0)