Skip to content

Commit dafbf94

Browse files
authored
Auto generate Techinal Name value (#200)
1 parent 2c0412a commit dafbf94

File tree

18 files changed

+177
-53
lines changed

18 files changed

+177
-53
lines changed

src/Modules/CrestApps.OrchardCore.AI.Mcp/Drivers/McpConnectionDisplayDriver.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public override async Task<IDisplayResult> UpdateAsync(McpConnection connection,
4242

4343
if (string.IsNullOrWhiteSpace(model.DisplayText))
4444
{
45-
context.Updater.ModelState.AddModelError(Prefix, nameof(model.DisplayText), S["The Display text is required."]);
45+
context.Updater.ModelState.AddModelError(Prefix, nameof(model.DisplayText), S["The Title is required."]);
4646
}
4747

4848
connection.DisplayText = model.DisplayText;

src/Modules/CrestApps.OrchardCore.AI.Mcp/Views/Connections/Index.cshtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@
131131
</div>
132132
</div>
133133

134-
<script asp-src="~/CrestApps.OrchardCore.AI/scripts/admin-ui.min.js" debug-src="~/CrestApps.OrchardCore.AI/scripts/admin-ui.min.js" asp-name="ai-admin-ui" at="Foot"></script>
134+
<script asp-name="ai-admin-ui" at="Foot"></script>
135135

136136
<script at="Foot" depends-on="ai-admin-ui">
137137
document.addEventListener('DOMContentLoaded', () => {

src/Modules/CrestApps.OrchardCore.AI.Mcp/Views/McpConnectionFields.Edit.cshtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
@model McpConnectionFieldsViewModel
66

77
<div class="@Orchard.GetWrapperClasses()">
8-
<label asp-for="DisplayText" class="@Orchard.GetLabelClasses()">@T["Display text"]</label>
8+
<label asp-for="DisplayText" class="@Orchard.GetLabelClasses()">@T["Title"]</label>
99
<div class="@Orchard.GetEndClasses()">
1010
<input type="text" asp-for="DisplayText" class="form-control" />
1111
<span asp-validation-for="DisplayText" class="text-danger"></span>

src/Modules/CrestApps.OrchardCore.AI/Assets.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,11 @@
44
"Assets/js/admin-ui.js"
55
],
66
"output": "wwwroot/scripts/admin-ui.js"
7+
},
8+
{
9+
"inputs": [
10+
"Assets/js/technical-name-generator.js"
11+
],
12+
"output": "wwwroot/scripts/technical-name-generator.js"
713
}
814
]
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
(function () {
2+
// Expose initializer globally so views can call it with specific field IDs.
3+
function toTitleCaseNoSpaces(value) {
4+
return (value || '')
5+
.trim()
6+
// split on whitespace or common separators or any non-alphanumeric
7+
.split(/[^A-Za-z0-9]+/)
8+
.filter(function (w) { return w.length > 0; })
9+
.map(function (w) { return w.charAt(0).toUpperCase() + w.slice(1).toLowerCase(); })
10+
.join('');
11+
}
12+
13+
function initAutoGenerateTechnicalName(displayId, nameId) {
14+
if (!displayId || !nameId) return;
15+
document.addEventListener('DOMContentLoaded', function () {
16+
var displayEl = document.getElementById(displayId);
17+
var nameEl = document.getElementById(nameId);
18+
if (!displayEl || !nameEl) return;
19+
20+
// Track whether the user has entered a non-empty value in the Name field.
21+
var userEdited = nameEl.value.trim() !== '';
22+
23+
// Resume auto-generation only when the Name field is empty again.
24+
nameEl.addEventListener('input', function () {
25+
userEdited = nameEl.value.trim() !== '';
26+
});
27+
28+
// Generate on keyup as requested
29+
displayEl.addEventListener('keyup', function () {
30+
if (userEdited) return;
31+
var generated = toTitleCaseNoSpaces(displayEl.value || '');
32+
nameEl.value = generated;
33+
});
34+
});
35+
}
36+
37+
// Automatically initialize for any elements using the data attributes.
38+
document.addEventListener('DOMContentLoaded', function () {
39+
var autoEls = document.querySelectorAll('[data-auto-tech-name]');
40+
for (var i = 0; i < autoEls.length; i++) {
41+
var el = autoEls[i];
42+
var displayId = el.getAttribute('data-display-id');
43+
var nameId = el.getAttribute('data-name-id');
44+
if (displayId && nameId) {
45+
// call initializer immediately (it will attach listeners)
46+
initAutoGenerateTechnicalName(displayId, nameId);
47+
}
48+
}
49+
});
50+
51+
window.initAutoGenerateTechnicalName = initAutoGenerateTechnicalName;
52+
})();

src/Modules/CrestApps.OrchardCore.AI/Drivers/AIProviderConnectionDisplayDriver.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public override async Task<IDisplayResult> UpdateAsync(AIProviderConnection conn
7979

8080
if (string.IsNullOrWhiteSpace(model.DisplayText))
8181
{
82-
context.Updater.ModelState.AddModelError(Prefix, nameof(model.DisplayText), S["The Display text is required."]);
82+
context.Updater.ModelState.AddModelError(Prefix, nameof(model.DisplayText), S["The Title is required."]);
8383
}
8484

8585
if (string.IsNullOrWhiteSpace(model.DefaultDeploymentName))
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using Microsoft.Extensions.Options;
2+
using OrchardCore.ResourceManagement;
3+
4+
namespace CrestApps.OrchardCore.AI;
5+
6+
internal sealed class ResourceManagementOptionsConfiguration : IConfigureOptions<ResourceManagementOptions>
7+
{
8+
private static readonly ResourceManifest _manifest;
9+
10+
static ResourceManagementOptionsConfiguration()
11+
{
12+
_manifest = new ResourceManifest();
13+
14+
_manifest
15+
.DefineScript("ai-admin-ui")
16+
.SetUrl("~/CrestApps.OrchardCore.AI/scripts/admin-ui.min.js", "~/CrestApps.OrchardCore.AI/scripts/admin-ui.js")
17+
.SetVersion("1.0.0");
18+
19+
_manifest
20+
.DefineScript("technical-name-generator")
21+
.SetUrl("~/CrestApps.OrchardCore.AI/scripts/technical-name-generator.min.js", "~/CrestApps.OrchardCore.AI/scripts/technical-name-generator.js")
22+
.SetVersion("1.0.0");
23+
}
24+
25+
public void Configure(ResourceManagementOptions options)
26+
{
27+
options.ResourceManifests.Add(_manifest);
28+
}
29+
}

src/Modules/CrestApps.OrchardCore.AI/Startup.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
using OrchardCore.Modules;
3131
using OrchardCore.Navigation;
3232
using OrchardCore.Recipes;
33+
using OrchardCore.ResourceManagement;
3334
using OrchardCore.Security.Permissions;
3435
using OrchardCore.Workflows.Helpers;
3536

@@ -64,6 +65,8 @@ public override void ConfigureServices(IServiceCollection services)
6465
.AddScoped<IAICompletionServiceHandler, FunctionInvocationAICompletionServiceHandler>();
6566

6667
services.AddDataMigration<AIProfileDefaultContextMigrations>();
68+
69+
services.AddTransient<IConfigureOptions<ResourceManagementOptions>, ResourceManagementOptionsConfiguration>();
6770
}
6871

6972
public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider)

src/Modules/CrestApps.OrchardCore.AI/Views/AIProfileMainFields.Edit.cshtml

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -30,38 +30,8 @@
3030

3131
@if (Model.IsNew)
3232
{
33-
<script>
34-
(function () {
35-
document.addEventListener('DOMContentLoaded', function () {
36-
var displayEl = document.getElementById('@Html.IdFor(x => x.DisplayText)');
37-
var nameEl = document.getElementById('@Html.IdFor(x => x.Name)');
38-
if (!displayEl || !nameEl) return;
39-
40-
// Track whether the user has entered a non-empty value in the Name field.
41-
var userEdited = nameEl.value.trim() !== '';
42-
43-
// Resume auto-generation only when the Name field is empty again.
44-
nameEl.addEventListener('input', function () {
45-
userEdited = nameEl.value.trim() !== '';
46-
});
47-
48-
function toTitleCaseNoSpaces(value) {
49-
return value
50-
.trim()
51-
// split on whitespace or common separators or any non-alphanumeric
52-
.split(/[^A-Za-z0-9]+/)
53-
.filter(function (w) { return w.length > 0; })
54-
.map(function (w) { return w.charAt(0).toUpperCase() + w.slice(1).toLowerCase(); })
55-
.join('');
56-
}
57-
58-
// Generate on keyup as requested
59-
displayEl.addEventListener('keyup', function () {
60-
if (userEdited) return;
61-
var generated = toTitleCaseNoSpaces(displayEl.value || '');
62-
nameEl.value = generated;
63-
});
64-
});
65-
})();
33+
<script asp-name="technical-name-generator"></script>
34+
<script depends-on="technical-name-generator">
35+
window.initAutoGenerateTechnicalName('@Html.IdFor(x => x.DisplayText)', '@Html.IdFor(x => x.Name)');
6636
</script>
6737
}

src/Modules/CrestApps.OrchardCore.AI/Views/AIProviderConnectionFields.Edit.cshtml

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,16 @@
44
@model AIProviderConnectionFieldsViewModel
55

66
<div class="@Orchard.GetWrapperClasses()">
7-
<label asp-for="Name" class="@Orchard.GetLabelClasses()">@T["Technical Name"]</label>
7+
<label asp-for="DisplayText" class="@Orchard.GetLabelClasses()">@T["Title"]</label>
8+
<div class="@Orchard.GetEndClasses()">
9+
<input type="text" asp-for="DisplayText" class="form-control" />
10+
<span asp-validation-for="DisplayText" class="text-danger"></span>
11+
<span class="hint">@T["The display text for the connection."]</span>
12+
</div>
13+
</div>
14+
15+
<div class="@Orchard.GetWrapperClasses()">
16+
<label asp-for="Name" class="@Orchard.GetLabelClasses()">@T["Technical name"]</label>
817
<div class="@Orchard.GetEndClasses()">
918
<input asp-for="Name" class="form-control" disabled="@(!Model.IsNew)" />
1019
<span asp-validation-for="Name" class="text-danger"></span>
@@ -19,15 +28,6 @@
1928
</div>
2029
</div>
2130

22-
<div class="@Orchard.GetWrapperClasses()">
23-
<label asp-for="DisplayText" class="@Orchard.GetLabelClasses()">@T["Display text"]</label>
24-
<div class="@Orchard.GetEndClasses()">
25-
<input type="text" asp-for="DisplayText" class="form-control" />
26-
<span asp-validation-for="DisplayText" class="text-danger"></span>
27-
<span class="hint">@T["The display text for the connection."]</span>
28-
</div>
29-
</div>
30-
3131
<div class="@Orchard.GetWrapperClasses()">
3232
<label asp-for="Type" class="@Orchard.GetLabelClasses()">@T["Type"]</label>
3333
<div class="@Orchard.GetEndClasses()">
@@ -55,3 +55,11 @@
5555
</div>
5656
</div>
5757
</div>
58+
59+
@if (Model.IsNew)
60+
{
61+
<script asp-name="technical-name-generator"></script>
62+
<script>
63+
window.initAutoGenerateTechnicalName('@Html.IdFor(x => x.DisplayText)', '@Html.IdFor(x => x.Name)');
64+
</script>
65+
}

0 commit comments

Comments
 (0)