@memberjunction/ng-dashboards 5.51.0 → 6.1.0-edge.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -0
- package/dist/AI/components/agents/agent-configuration.component.d.ts.map +1 -1
- package/dist/AI/components/agents/agent-configuration.component.js +59 -63
- package/dist/AI/components/agents/agent-configuration.component.js.map +1 -1
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts +7 -7
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js +21 -21
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js.map +1 -1
- package/dist/AI/components/autotagging/shared/classify.types.d.ts +4 -4
- package/dist/AI/components/autotagging/shared/classify.types.d.ts.map +1 -1
- package/dist/AI/components/autotagging/shared/classify.types.js.map +1 -1
- package/dist/AI/components/autotagging/tabs/sources-tab.component.d.ts +14 -19
- package/dist/AI/components/autotagging/tabs/sources-tab.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/tabs/sources-tab.component.js +53 -77
- package/dist/AI/components/autotagging/tabs/sources-tab.component.js.map +1 -1
- package/dist/AI/components/models/model-management.component.d.ts.map +1 -1
- package/dist/AI/components/models/model-management.component.js +67 -70
- package/dist/AI/components/models/model-management.component.js.map +1 -1
- package/dist/AI/components/prompts/prompt-management.component.d.ts.map +1 -1
- package/dist/AI/components/prompts/prompt-management.component.js +55 -59
- package/dist/AI/components/prompts/prompt-management.component.js.map +1 -1
- package/dist/AI/components/system/system-configuration.component.d.ts.map +1 -1
- package/dist/AI/components/system/system-configuration.component.js +59 -63
- package/dist/AI/components/system/system-configuration.component.js.map +1 -1
- package/dist/AI/components/tags/tags-resource.component.d.ts +15 -20
- package/dist/AI/components/tags/tags-resource.component.d.ts.map +1 -1
- package/dist/AI/components/tags/tags-resource.component.js +51 -77
- package/dist/AI/components/tags/tags-resource.component.js.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts +1 -3
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.js +22 -43
- package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -1
- package/dist/Actions/components/explorer/action-card.component.js +6 -6
- package/dist/Actions/components/explorer/action-card.component.js.map +1 -1
- package/dist/Actions/components/explorer/action-list-item.component.js +3 -3
- package/dist/Actions/components/explorer/action-list-item.component.js.map +1 -1
- package/dist/Actions/index.d.ts +0 -1
- package/dist/Actions/index.d.ts.map +1 -1
- package/dist/Actions/index.js +0 -1
- package/dist/Actions/index.js.map +1 -1
- package/dist/Admin/admin-dev-tools-resource.component.d.ts.map +1 -1
- package/dist/Admin/admin-dev-tools-resource.component.js +14 -0
- package/dist/Admin/admin-dev-tools-resource.component.js.map +1 -1
- package/dist/Admin/base-admin-container.component.d.ts.map +1 -1
- package/dist/Admin/base-admin-container.component.js +15 -0
- package/dist/Admin/base-admin-container.component.js.map +1 -1
- package/dist/DevTools/grid-width-lab.component.d.ts +78 -0
- package/dist/DevTools/grid-width-lab.component.d.ts.map +1 -0
- package/dist/DevTools/grid-width-lab.component.js +248 -0
- package/dist/DevTools/grid-width-lab.component.js.map +1 -0
- package/dist/DevTools/index.d.ts +2 -0
- package/dist/DevTools/index.d.ts.map +1 -1
- package/dist/DevTools/index.js +2 -0
- package/dist/DevTools/index.js.map +1 -1
- package/dist/DevTools/tab-strip-lab.component.d.ts +76 -0
- package/dist/DevTools/tab-strip-lab.component.d.ts.map +1 -0
- package/dist/DevTools/tab-strip-lab.component.js +247 -0
- package/dist/DevTools/tab-strip-lab.component.js.map +1 -0
- package/dist/Home/home-application.d.ts +5 -0
- package/dist/Home/home-application.d.ts.map +1 -1
- package/dist/Home/home-application.js +9 -0
- package/dist/Home/home-application.js.map +1 -1
- package/dist/Home/home-dashboard.component.d.ts.map +1 -1
- package/dist/Home/home-dashboard.component.js +0 -6
- package/dist/Home/home-dashboard.component.js.map +1 -1
- package/dist/SystemDiagnostics/system-diagnostics.component.d.ts +7 -3
- package/dist/SystemDiagnostics/system-diagnostics.component.d.ts.map +1 -1
- package/dist/SystemDiagnostics/system-diagnostics.component.js +22 -21
- package/dist/SystemDiagnostics/system-diagnostics.component.js.map +1 -1
- package/dist/Testing/components/testing-explorer.component.d.ts +9 -0
- package/dist/Testing/components/testing-explorer.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-explorer.component.js +28 -5
- package/dist/Testing/components/testing-explorer.component.js.map +1 -1
- package/dist/actions-dashboards.module.d.ts +23 -24
- package/dist/actions-dashboards.module.d.ts.map +1 -1
- package/dist/actions-dashboards.module.js +0 -5
- package/dist/actions-dashboards.module.js.map +1 -1
- package/dist/ai-dashboards.module.d.ts +1 -1
- package/dist/ai-dashboards.module.d.ts.map +1 -1
- package/dist/ai-dashboards.module.js +4 -1
- package/dist/ai-dashboards.module.js.map +1 -1
- package/dist/core-dashboards.module.d.ts +46 -42
- package/dist/core-dashboards.module.d.ts.map +1 -1
- package/dist/core-dashboards.module.js +22 -0
- package/dist/core-dashboards.module.js.map +1 -1
- package/dist/public-api.d.ts +1 -1
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +1 -1
- package/dist/public-api.js.map +1 -1
- package/dist/shared/action-scheduled-job.d.ts +58 -0
- package/dist/shared/action-scheduled-job.d.ts.map +1 -0
- package/dist/shared/action-scheduled-job.js +77 -0
- package/dist/shared/action-scheduled-job.js.map +1 -0
- package/package.json +73 -70
- package/dist/Actions/components/scheduled-actions.component.d.ts +0 -16
- package/dist/Actions/components/scheduled-actions.component.d.ts.map +0 -1
- package/dist/Actions/components/scheduled-actions.component.js +0 -57
- package/dist/Actions/components/scheduled-actions.component.js.map +0 -1
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Self-contained sub-page: owns its header-interior, the source cards grid,
|
|
5
5
|
* the per-source detail slide-in, and the quick-schedule dialog. Receives the
|
|
6
6
|
* shared raw data needed to build the cards (sources, items, tags, runs, and
|
|
7
|
-
* the cached
|
|
7
|
+
* the cached Scheduled Job + EntityRecordDocument lookups) from the host
|
|
8
8
|
* orchestrator via `@Input()`. Cross-tab concerns — opening the add/edit slide-in
|
|
9
9
|
* form, running the pipeline, opening a content-item detail, and reloading the
|
|
10
10
|
* shared source list after a mutation — bubble up via `@Output()`.
|
|
@@ -20,6 +20,7 @@ import { MJNotificationService } from '@memberjunction/ng-notifications';
|
|
|
20
20
|
import { MJConfirmService } from '@memberjunction/ng-ui-components';
|
|
21
21
|
import { AIEngineBase } from '@memberjunction/ai-engine-base';
|
|
22
22
|
import { KnowledgeHubMetadataEngine } from '@memberjunction/core-entities';
|
|
23
|
+
import { BuildSingleStaticParamConfiguration, ResolveActionJobTypeID } from '../../../../shared/action-scheduled-job';
|
|
23
24
|
import { formatNumber, formatDate, computeDuration, displayStatus, getSourceTypeIcon, CronToHumanReadable, deriveDisplayName } from '../shared/classify.format';
|
|
24
25
|
import * as i0 from "@angular/core";
|
|
25
26
|
import * as i1 from "@angular/forms";
|
|
@@ -188,9 +189,9 @@ function ClassifySourcesTabComponent_For_20_Template(rf, ctx) { if (rf & 1) {
|
|
|
188
189
|
i0.ɵɵadvance(5);
|
|
189
190
|
i0.ɵɵtextInterpolate(card_r4.LastRunAgo);
|
|
190
191
|
i0.ɵɵadvance(3);
|
|
191
|
-
i0.ɵɵconditional(card_r4.
|
|
192
|
+
i0.ɵɵconditional(card_r4.ScheduledJobID ? 35 : -1);
|
|
192
193
|
i0.ɵɵadvance(11);
|
|
193
|
-
i0.ɵɵconditional(!card_r4.
|
|
194
|
+
i0.ɵɵconditional(!card_r4.ScheduledJobID ? 46 : -1);
|
|
194
195
|
} }
|
|
195
196
|
function ClassifySourcesTabComponent_Conditional_27_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
196
197
|
i0.ɵɵelementStart(0, "div", 10);
|
|
@@ -655,14 +656,14 @@ export class ClassifySourcesTabComponent extends BaseAngularComponent {
|
|
|
655
656
|
get TotalTagCount() {
|
|
656
657
|
return this._totalTagCount;
|
|
657
658
|
}
|
|
658
|
-
/** Cached
|
|
659
|
-
|
|
660
|
-
set
|
|
661
|
-
this.
|
|
659
|
+
/** Cached Scheduled Job entities, keyed by normalized ID, supplied by the host. */
|
|
660
|
+
_scheduledJobs = new Map();
|
|
661
|
+
set ScheduledJobs(value) {
|
|
662
|
+
this._scheduledJobs = value ?? new Map();
|
|
662
663
|
this.rebuild();
|
|
663
664
|
}
|
|
664
|
-
get
|
|
665
|
-
return this.
|
|
665
|
+
get ScheduledJobs() {
|
|
666
|
+
return this._scheduledJobs;
|
|
666
667
|
}
|
|
667
668
|
/** Cache: EntityRecordDocument ID → RecordID, supplied by the host. */
|
|
668
669
|
_entityRecordDocCache = new Map();
|
|
@@ -762,9 +763,9 @@ export class ClassifySourcesTabComponent extends BaseAngularComponent {
|
|
|
762
763
|
const typeName = source['ContentSourceType'] ?? 'Unknown';
|
|
763
764
|
const lastRunStatus = lastRun ? lastRun['Status']?.toLowerCase() : null;
|
|
764
765
|
const hasError = lastRunStatus === 'error' || lastRunStatus === 'failed';
|
|
765
|
-
const
|
|
766
|
-
const
|
|
767
|
-
const cronExpr =
|
|
766
|
+
const scheduledJobID = source['ScheduledJobID'] ?? null;
|
|
767
|
+
const scheduledJobName = source['ScheduledJob'] ?? null;
|
|
768
|
+
const cronExpr = scheduledJobID ? this.getScheduledJobCron(scheduledJobID) : null;
|
|
768
769
|
return {
|
|
769
770
|
ID: id,
|
|
770
771
|
Name: source['Name'] ?? 'Unnamed Source',
|
|
@@ -787,8 +788,8 @@ export class ClassifySourcesTabComponent extends BaseAngularComponent {
|
|
|
787
788
|
EntityID: source['EntityID'] ?? '',
|
|
788
789
|
EntityDocumentID: source['EntityDocumentID'] ?? '',
|
|
789
790
|
RequiresFileType: this.sourceTypeRequiresFileType(source['ContentSourceTypeID']),
|
|
790
|
-
|
|
791
|
-
|
|
791
|
+
ScheduledJobID: scheduledJobID,
|
|
792
|
+
ScheduledJobName: scheduledJobName,
|
|
792
793
|
ScheduleDescription: cronExpr ? CronToHumanReadable(cronExpr) : null,
|
|
793
794
|
};
|
|
794
795
|
});
|
|
@@ -959,9 +960,9 @@ export class ClassifySourcesTabComponent extends BaseAngularComponent {
|
|
|
959
960
|
const diffDays = Math.floor(diffHours / 24);
|
|
960
961
|
return `${diffDays}d ago`;
|
|
961
962
|
}
|
|
962
|
-
/** Looks up the cron expression for a cached
|
|
963
|
-
|
|
964
|
-
const cached = this.
|
|
963
|
+
/** Looks up the cron expression for a cached Scheduled Job by ID */
|
|
964
|
+
getScheduledJobCron(scheduledJobID) {
|
|
965
|
+
const cached = this._scheduledJobs.get(NormalizeUUID(scheduledJobID));
|
|
965
966
|
return cached?.CronExpression ?? null;
|
|
966
967
|
}
|
|
967
968
|
/** Returns the CSS class for a pipeline status badge color */
|
|
@@ -1013,8 +1014,8 @@ export class ClassifySourcesTabComponent extends BaseAngularComponent {
|
|
|
1013
1014
|
this.cdr.detectChanges();
|
|
1014
1015
|
}
|
|
1015
1016
|
/**
|
|
1016
|
-
* Saves a new
|
|
1017
|
-
* ContentSource.
|
|
1017
|
+
* Saves a new Scheduled Job (type: Action) for the current source, links it via
|
|
1018
|
+
* ContentSource.ScheduledJobID, and creates the default action params
|
|
1018
1019
|
* for the Autotag and Vectorize action.
|
|
1019
1020
|
*/
|
|
1020
1021
|
async SaveSchedule() {
|
|
@@ -1031,29 +1032,33 @@ export class ClassifySourcesTabComponent extends BaseAngularComponent {
|
|
|
1031
1032
|
MJNotificationService.Instance.CreateSimpleNotification('Could not find the "Autotag and Vectorize Content" action. Please check action configuration.', 'error', 5000);
|
|
1032
1033
|
return;
|
|
1033
1034
|
}
|
|
1034
|
-
// 2.
|
|
1035
|
-
const
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1035
|
+
// 2. Resolve the 'Action' scheduled-job type
|
|
1036
|
+
const jobTypeID = await ResolveActionJobTypeID(p);
|
|
1037
|
+
if (!jobTypeID) {
|
|
1038
|
+
MJNotificationService.Instance.CreateSimpleNotification('Could not find the "Action" scheduled job type. Please check scheduling configuration.', 'error', 5000);
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
// 3. Create the Scheduled Job. The action and its parameters ride in Configuration —
|
|
1042
|
+
// ActionScheduledJobDriver reads them from there (no child param rows any more).
|
|
1043
|
+
const scheduledJob = await p.GetEntityObject('MJ: Scheduled Jobs', p.CurrentUser);
|
|
1044
|
+
scheduledJob.NewRecord();
|
|
1045
|
+
scheduledJob.JobTypeID = jobTypeID;
|
|
1046
|
+
scheduledJob.Name = `Autotag: ${card.Name}`;
|
|
1047
|
+
scheduledJob.Description = `Automated classification pipeline for content source "${card.Name}"`;
|
|
1048
|
+
scheduledJob.CronExpression = this.ScheduleCron;
|
|
1049
|
+
scheduledJob.Status = this.ScheduleEnabled ? 'Active' : 'Disabled';
|
|
1050
|
+
scheduledJob.Timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
1051
|
+
scheduledJob.Configuration = await BuildSingleStaticParamConfiguration(p, actionID, 'EntityNames', card.ID, '[Classify]');
|
|
1052
|
+
const saved = await scheduledJob.Save();
|
|
1046
1053
|
if (!saved) {
|
|
1047
|
-
const errorDetail =
|
|
1054
|
+
const errorDetail = scheduledJob.LatestResult?.CompleteMessage ?? 'Unknown error';
|
|
1048
1055
|
MJNotificationService.Instance.CreateSimpleNotification(`Failed to create schedule: ${errorDetail}`, 'error', 5000);
|
|
1049
1056
|
return;
|
|
1050
1057
|
}
|
|
1051
|
-
//
|
|
1052
|
-
await this.
|
|
1053
|
-
//
|
|
1054
|
-
|
|
1055
|
-
// 5. Cache the new action for cron display
|
|
1056
|
-
this._scheduledActions.set(NormalizeUUID(scheduledAction.ID), scheduledAction);
|
|
1058
|
+
// 4. Link the Scheduled Job to the ContentSource
|
|
1059
|
+
await this.linkScheduleToSource(card.ID, scheduledJob.ID);
|
|
1060
|
+
// 5. Cache the new job for cron display
|
|
1061
|
+
this._scheduledJobs.set(NormalizeUUID(scheduledJob.ID), scheduledJob);
|
|
1057
1062
|
MJNotificationService.Instance.CreateSimpleNotification(`Schedule created: ${CronToHumanReadable(this.ScheduleCron)}`, 'success', 3000);
|
|
1058
1063
|
this.CloseScheduleDialog();
|
|
1059
1064
|
this.DataChanged.emit();
|
|
@@ -1069,10 +1074,10 @@ export class ClassifySourcesTabComponent extends BaseAngularComponent {
|
|
|
1069
1074
|
}
|
|
1070
1075
|
}
|
|
1071
1076
|
/**
|
|
1072
|
-
* Removes the schedule from a source card by unlinking the
|
|
1077
|
+
* Removes the schedule from a source card by unlinking the ScheduledJobID.
|
|
1073
1078
|
*/
|
|
1074
1079
|
async RemoveSchedule(card) {
|
|
1075
|
-
if (!card.
|
|
1080
|
+
if (!card.ScheduledJobID)
|
|
1076
1081
|
return;
|
|
1077
1082
|
if (!(await this.confirmService.ConfirmDelete({ title: 'Remove Schedule', message: `Remove the schedule "${card.ScheduleDescription ?? 'schedule'}" from "${card.Name}"?`, confirmText: 'Remove' })))
|
|
1078
1083
|
return;
|
|
@@ -1110,43 +1115,14 @@ export class ClassifySourcesTabComponent extends BaseAngularComponent {
|
|
|
1110
1115
|
return null;
|
|
1111
1116
|
}
|
|
1112
1117
|
/**
|
|
1113
|
-
*
|
|
1114
|
-
*
|
|
1115
|
-
*/
|
|
1116
|
-
async createSourceIDParam(scheduledActionID, actionID, sourceID) {
|
|
1117
|
-
const rv = RunView.FromMetadataProvider(this.ProviderToUse);
|
|
1118
|
-
const paramResult = await rv.RunView({
|
|
1119
|
-
EntityName: 'MJ: Action Params',
|
|
1120
|
-
ExtraFilter: `ActionID = '${actionID}' AND Name = 'EntityNames'`,
|
|
1121
|
-
Fields: ['ID', 'Name'],
|
|
1122
|
-
ResultType: 'simple',
|
|
1123
|
-
MaxRows: 1,
|
|
1124
|
-
});
|
|
1125
|
-
if (!paramResult.Success || paramResult.Results.length === 0) {
|
|
1126
|
-
console.warn('[Classify] Could not find EntityNames action param for source ID scheduling');
|
|
1127
|
-
return;
|
|
1128
|
-
}
|
|
1129
|
-
const p = this.ProviderToUse;
|
|
1130
|
-
const param = await p.GetEntityObject('MJ: Scheduled Action Params', p.CurrentUser);
|
|
1131
|
-
param.NewRecord();
|
|
1132
|
-
param.ScheduledActionID = scheduledActionID;
|
|
1133
|
-
param.ActionParamID = paramResult.Results[0].ID;
|
|
1134
|
-
param.ValueType = 'Static';
|
|
1135
|
-
param.Value = sourceID;
|
|
1136
|
-
const saved = await param.Save();
|
|
1137
|
-
if (!saved) {
|
|
1138
|
-
console.warn('[Classify] Failed to save schedule param:', param.LatestResult?.CompleteMessage);
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
|
-
/**
|
|
1142
|
-
* Links (or unlinks) a ScheduledAction to a ContentSource by updating
|
|
1143
|
-
* the ContentSource.ScheduledActionID field.
|
|
1118
|
+
* Links (or unlinks) a Scheduled Job to a ContentSource by updating
|
|
1119
|
+
* the ContentSource.ScheduledJobID field.
|
|
1144
1120
|
*/
|
|
1145
|
-
async linkScheduleToSource(sourceID,
|
|
1121
|
+
async linkScheduleToSource(sourceID, scheduledJobID) {
|
|
1146
1122
|
const p = this.ProviderToUse;
|
|
1147
1123
|
const entity = await p.GetEntityObject('MJ: Content Sources', p.CurrentUser);
|
|
1148
1124
|
await entity.InnerLoad(new CompositeKey([{ FieldName: 'ID', Value: sourceID }]));
|
|
1149
|
-
entity.
|
|
1125
|
+
entity.ScheduledJobID = scheduledJobID;
|
|
1150
1126
|
const saved = await entity.Save();
|
|
1151
1127
|
if (!saved) {
|
|
1152
1128
|
throw new Error(entity.LatestResult?.CompleteMessage ?? 'Failed to update content source');
|
|
@@ -1362,7 +1338,7 @@ export class ClassifySourcesTabComponent extends BaseAngularComponent {
|
|
|
1362
1338
|
});
|
|
1363
1339
|
}
|
|
1364
1340
|
static ɵfac = /*@__PURE__*/ (() => { let ɵClassifySourcesTabComponent_BaseFactory; return function ClassifySourcesTabComponent_Factory(__ngFactoryType__) { return (ɵClassifySourcesTabComponent_BaseFactory || (ɵClassifySourcesTabComponent_BaseFactory = i0.ɵɵgetInheritedFactory(ClassifySourcesTabComponent)))(__ngFactoryType__ || ClassifySourcesTabComponent); }; })();
|
|
1365
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ClassifySourcesTabComponent, selectors: [["classify-sources-tab"]], inputs: { Sources: "Sources", Items: "Items", Tags: "Tags", Runs: "Runs", TotalItemCount: "TotalItemCount", TotalTagCount: "TotalTagCount",
|
|
1341
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ClassifySourcesTabComponent, selectors: [["classify-sources-tab"]], inputs: { Sources: "Sources", Items: "Items", Tags: "Tags", Runs: "Runs", TotalItemCount: "TotalItemCount", TotalTagCount: "TotalTagCount", ScheduledJobs: "ScheduledJobs", EntityRecordDocCache: "EntityRecordDocCache" }, outputs: { AddSourceRequested: "AddSourceRequested", AddSourceGuidedRequested: "AddSourceGuidedRequested", EditSourceRequested: "EditSourceRequested", RunSourceRequested: "RunSourceRequested", OpenContentItemRequested: "OpenContentItemRequested", DataChanged: "DataChanged" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 30, vars: 11, consts: [["Title", "Content Sources", "Subtitle", "Configure where content is ingested from"], ["actions", ""], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "click", "disabled"], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "click"], [1, "fa-solid", "fa-wand-magic-sparkles"], [1, "mj-action-label"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click"], [1, "fa-solid", "fa-plus"], [3, "Padding", "Flex"], [1, "at-main-area"], [1, "at-loading-overlay"], [1, "at-page-body"], ["Icon", "fa-solid fa-database", "Title", "No content sources yet", "Message", "Use the guided setup to stand up your first source \u2014 pick an entity or URL, configure its document, taxonomy, and domain context in one flow.", 1, "at-sources-empty"], [1, "at-sources-grid"], [1, "at-source-card-full", "at-source-card-clickable"], [1, "at-add-source-card", 3, "click"], [1, "fa-solid", "fa-plus-circle"], [2, "font-size", "0.85rem", "font-weight", "600"], [2, "font-size", "0.72rem"], [1, "at-schedule-overlay"], [3, "Closed", "Provider", "Show", "SourceCard"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-arrows-rotate"], ["text", "Refreshing sources\u2026"], ["ngProjectAs", "[actions]", 5, ["", "actions", ""], 1, "at-sources-empty-actions"], [1, "at-source-card-full", "at-source-card-clickable", 3, "click"], [1, "at-source-card-header"], [1, "at-source-card-icon"], [1, "at-source-card-title"], [1, "at-source-card-type"], [1, "at-source-card-status"], [1, "at-source-mini-status"], [1, "at-source-card-url"], [1, "at-source-card-stats"], [1, "at-source-stat"], [1, "at-source-stat-value"], [1, "at-source-stat-label"], ["title", "Click to remove schedule", 1, "at-schedule-indicator"], [1, "at-source-card-actions"], [1, "at-source-action-btn", 3, "click"], [1, "fa-solid", "fa-pen"], [1, "fa-solid", "fa-play"], [1, "fa-solid", "fa-flask"], [1, "at-source-action-btn", "at-source-schedule-btn"], [1, "at-source-action-btn", "at-source-delete-btn", 3, "click"], [1, "fa-solid", "fa-trash"], ["title", "Click to remove schedule", 1, "at-schedule-indicator", 3, "click"], [1, "fa-regular", "fa-clock"], [1, "at-source-action-btn", "at-source-schedule-btn", 3, "click"], [1, "at-slide-overlay", 3, "click"], [1, "at-slide-panel", "at-detail-panel"], [1, "at-slide-header"], [1, "fa-solid", "fa-database"], ["aria-label", "Close source detail", 1, "at-slide-close", 3, "click"], [1, "fa-solid", "fa-times"], [1, "at-slide-body"], ["text", "Loading source details..."], [1, "at-detail-source-header"], [1, "at-detail-item-name"], [1, "at-detail-badges"], [1, "at-detail-badge", "at-detail-badge-type"], [1, "at-detail-badge"], [1, "at-detail-section"], [1, "at-detail-section-label"], [1, "at-detail-meta-grid"], [1, "at-detail-meta-row"], [1, "at-detail-meta-key"], [1, "at-detail-meta-value"], [1, "at-detail-stats-strip"], [1, "at-detail-stat"], [1, "at-detail-stat-value"], [1, "at-detail-stat-label"], [1, "at-detail-section-header-row"], [1, "at-detail-section-controls"], [1, "at-detail-filter-select", 3, "ngModelChange", "ngModel"], [3, "value"], ["title", "Re-queue failed items for processing", 1, "at-action-btn", "at-retry-btn", 3, "click"], [1, "fa-solid", "fa-rotate-right"], [1, "at-detail-content-list"], ["Size", "compact", 3, "Variant", "Title"], [1, "at-detail-content-item"], [1, "at-detail-pagination"], [1, "at-detail-actions"], [1, "at-action-btn", "at-primary-btn", 3, "click"], [1, "at-action-btn", "at-secondary-btn", 3, "click"], [1, "at-action-btn", "at-secondary-btn", "at-source-delete-btn", 3, "click"], ["target", "_blank", 1, "at-detail-link", "at-detail-meta-value", 3, "href"], [1, "at-detail-content-item", 3, "click"], [1, "at-feed-status-dot"], [1, "at-detail-content-item-name"], [1, "at-status-badge"], [1, "at-detail-content-item-tags"], [1, "at-detail-content-item-time"], [1, "at-page-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-chevron-left"], [1, "at-page-info"], [1, "fa-solid", "fa-chevron-right"], [1, "at-detail-run-history"], [1, "at-detail-run-row"], [1, "at-run-status-badge"], [1, "at-detail-run-time"], [1, "at-detail-run-duration"], [1, "at-detail-run-items"], [1, "at-schedule-overlay", 3, "click"], [1, "at-schedule-dialog", 3, "click"], [1, "at-schedule-dialog-header"], ["aria-label", "Close", 1, "at-schedule-dialog-close", 3, "click"], [1, "at-schedule-dialog-body"], [1, "at-schedule-field"], [1, "at-schedule-source-name"], [1, "at-schedule-action-name"], ["for", "schedule-cron"], ["id", "schedule-cron", "type", "text", "placeholder", "0 2 * * *", 1, "mj-input", "at-schedule-cron-input", 3, "ngModelChange", "ngModel"], [1, "at-schedule-cron-preview"], [1, "fa-solid", "fa-info-circle"], [1, "at-schedule-field", "at-schedule-toggle-row"], ["for", "schedule-enabled"], ["id", "schedule-enabled", "type", "checkbox", 1, "mj-checkbox", 3, "ngModelChange", "ngModel"], [1, "at-schedule-dialog-footer"], [1, "at-action-btn", "at-primary-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-check"]], template: function ClassifySourcesTabComponent_Template(rf, ctx) { if (rf & 1) {
|
|
1366
1342
|
i0.ɵɵelementStart(0, "mj-page-header-interior", 0)(1, "div", 1)(2, "button", 2);
|
|
1367
1343
|
i0.ɵɵlistener("click", function ClassifySourcesTabComponent_Template_button_click_2_listener() { return ctx.Refresh(); });
|
|
1368
1344
|
i0.ɵɵconditionalCreate(3, ClassifySourcesTabComponent_Conditional_3_Template, 3, 0)(4, ClassifySourcesTabComponent_Conditional_4_Template, 3, 0);
|
|
@@ -1422,7 +1398,7 @@ export class ClassifySourcesTabComponent extends BaseAngularComponent {
|
|
|
1422
1398
|
}
|
|
1423
1399
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ClassifySourcesTabComponent, [{
|
|
1424
1400
|
type: Component,
|
|
1425
|
-
args: [{ standalone: false, selector: 'classify-sources-tab', template: "<mj-page-header-interior\n Title=\"Content Sources\"\n Subtitle=\"Configure where content is ingested from\">\n <div actions>\n <button mjButton variant=\"secondary\" size=\"sm\" (click)=\"Refresh()\" [disabled]=\"IsLoading\">\n @if (IsLoading) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> <span class=\"mj-action-label\">Refreshing\u2026</span>\n } @else {\n <i class=\"fa-solid fa-arrows-rotate\"></i> <span class=\"mj-action-label\">Refresh</span>\n }\n </button>\n <button mjButton variant=\"secondary\" size=\"sm\" (click)=\"onAddSourceGuided()\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i> <span class=\"mj-action-label\">Add Source (Guided)</span>\n </button>\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"onAddSource()\">\n <i class=\"fa-solid fa-plus\"></i> <span class=\"mj-action-label\">Add Source</span>\n </button>\n </div>\n</mj-page-header-interior>\n\n<mj-page-body-interior [Padding]=\"false\" [Flex]=\"true\">\n <div class=\"at-main-area\">\n @if (IsLoading) {\n <div class=\"at-loading-overlay\">\n <mj-loading text=\"Refreshing sources\u2026\"></mj-loading>\n </div>\n }\n <div class=\"at-page-body\">\n @if (SourceCards.length === 0 && !IsLoading) {\n <mj-empty-state class=\"at-sources-empty\" Icon=\"fa-solid fa-database\"\n Title=\"No content sources yet\"\n Message=\"Use the guided setup to stand up your first source \u2014 pick an entity or URL, configure its document, taxonomy, and domain context in one flow.\">\n <div class=\"at-sources-empty-actions\" ngProjectAs=\"[actions]\">\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"onAddSourceGuided()\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i> Add Source (Guided)\n </button>\n <button mjButton variant=\"secondary\" size=\"sm\" (click)=\"onAddSource()\">\n <i class=\"fa-solid fa-plus\"></i> Add manually\n </button>\n </div>\n </mj-empty-state>\n }\n\n <div class=\"at-sources-grid\">\n @for (card of SourceCards; track card.ID) {\n <div class=\"at-source-card-full at-source-card-clickable\" (click)=\"OpenSourceDetail(card)\">\n <div class=\"at-source-card-header\">\n <div class=\"at-source-card-icon\"><i [class]=\"card.Icon\"></i></div>\n <div>\n <div class=\"at-source-card-title\">{{ card.Name }}</div>\n <div class=\"at-source-card-type\">{{ card.SourceTypeName }}@if (card.RequiresFileType) { \u00B7 {{ card.FileTypeName }}}</div>\n </div>\n <div class=\"at-source-card-status\" [class]=\"card.StatusClass\">\n <div class=\"at-source-mini-status\" [class]=\"card.StatusClass\"></div>\n {{ card.StatusLabel }}\n </div>\n </div>\n @if (card.URL) {\n <div class=\"at-source-card-url\">{{ card.URL }}</div>\n }\n <div class=\"at-source-card-stats\">\n <div class=\"at-source-stat\">\n <div class=\"at-source-stat-value\">{{ formatNumber(card.ItemCount) }}</div>\n <div class=\"at-source-stat-label\">Items</div>\n </div>\n <div class=\"at-source-stat\">\n <div class=\"at-source-stat-value\">{{ formatNumber(card.TagCount) }}</div>\n <div class=\"at-source-stat-label\">Tags</div>\n </div>\n <div class=\"at-source-stat\">\n <div class=\"at-source-stat-value\">{{ card.AvgTags }}</div>\n <div class=\"at-source-stat-label\">Avg Tags</div>\n </div>\n <div class=\"at-source-stat\">\n <div class=\"at-source-stat-value\">{{ card.LastRunAgo }}</div>\n <div class=\"at-source-stat-label\">Last Run</div>\n </div>\n </div>\n <!-- Schedule indicator -->\n @if (card.ScheduledActionID) {\n <div class=\"at-schedule-indicator\" (click)=\"RemoveSchedule(card); $event.stopPropagation()\" title=\"Click to remove schedule\">\n <i class=\"fa-regular fa-clock\"></i>\n <span>{{ GetScheduleLabel(card) }}</span>\n </div>\n }\n <div class=\"at-source-card-actions\">\n <button class=\"at-source-action-btn\" (click)=\"onEditSource(card); $event.stopPropagation()\"><i class=\"fa-solid fa-pen\"></i> Edit</button>\n <button class=\"at-source-action-btn\" (click)=\"onRunSource(card.ID); $event.stopPropagation()\"><i class=\"fa-solid fa-play\"></i> Run</button>\n <button class=\"at-source-action-btn\" (click)=\"OpenDryRunPreview(card); $event.stopPropagation()\"><i class=\"fa-solid fa-flask\"></i> Preview</button>\n @if (!card.ScheduledActionID) {\n <button class=\"at-source-action-btn at-source-schedule-btn\" (click)=\"OpenScheduleDialog(card); $event.stopPropagation()\"><i class=\"fa-regular fa-clock\"></i> Schedule</button>\n }\n <button class=\"at-source-action-btn at-source-delete-btn\" (click)=\"DeleteSource(card); $event.stopPropagation()\"><i class=\"fa-solid fa-trash\"></i> Delete</button>\n </div>\n </div>\n }\n\n <!-- Add Source Card -->\n <div class=\"at-add-source-card\" (click)=\"onAddSource()\">\n <i class=\"fa-solid fa-plus-circle\"></i>\n <span style=\"font-size: 0.85rem; font-weight: 600;\">Add Content Source</span>\n <span style=\"font-size: 0.72rem;\">Web, RSS, Email, Files, API</span>\n </div>\n </div>\n </div>\n </div>\n</mj-page-body-interior>\n\n<!-- \u2550\u2550\u2550\u2550\u2550\u2550 SOURCE DETAIL SLIDE-IN \u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (ShowSourceDetail) {\n <div class=\"at-slide-overlay\" (click)=\"CloseSourceDetail()\"></div>\n <div class=\"at-slide-panel at-detail-panel\">\n <div class=\"at-slide-header\">\n <h3><i class=\"fa-solid fa-database\"></i> Source Detail</h3>\n <button class=\"at-slide-close\" aria-label=\"Close source detail\" (click)=\"CloseSourceDetail()\"><i class=\"fa-solid fa-times\"></i></button>\n </div>\n <div class=\"at-slide-body\">\n @if (SourceDetailLoading) {\n <div class=\"at-loading-overlay\">\n <mj-loading text=\"Loading source details...\"></mj-loading>\n </div>\n }\n @if (!SourceDetailLoading && SelectedSource) {\n <!-- Source Header -->\n <div class=\"at-detail-source-header\">\n <div class=\"at-source-card-icon\"><i [class]=\"SelectedSource.Icon\"></i></div>\n <div>\n <h4 class=\"at-detail-item-name\">{{ SelectedSource.Name }}</h4>\n <div class=\"at-detail-badges\">\n <span class=\"at-detail-badge at-detail-badge-type\">{{ SelectedSource.SourceTypeName }}</span>\n <span class=\"at-detail-badge\" [class]=\"'at-detail-badge-status-' + SelectedSource.StatusClass\">\n {{ SelectedSource.StatusLabel }}\n </span>\n </div>\n </div>\n </div>\n\n <!-- Configuration -->\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-label\">Configuration</div>\n <div class=\"at-detail-meta-grid\">\n @if (SelectedSource.URL) {\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">URL</span>\n <a [href]=\"SelectedSource.URL\" target=\"_blank\" class=\"at-detail-link at-detail-meta-value\">{{ SelectedSource.URL }}</a>\n </div>\n }\n @if (SelectedSource.RequiresFileType) {\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">Content Type</span>\n <span class=\"at-detail-meta-value\">{{ SelectedSource.ContentTypeName }}</span>\n </div>\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">File Type</span>\n <span class=\"at-detail-meta-value\">{{ SelectedSource.FileTypeName }}</span>\n </div>\n }\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">Embedding Model</span>\n <span class=\"at-detail-meta-value\">{{ SelectedSource.EmbeddingModelName }}</span>\n </div>\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">Vector Index</span>\n <span class=\"at-detail-meta-value\">{{ SelectedSource.VectorIndexName }}</span>\n </div>\n </div>\n </div>\n\n <!-- Stats -->\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-label\">Statistics</div>\n <div class=\"at-detail-stats-strip\">\n <div class=\"at-detail-stat\">\n <div class=\"at-detail-stat-value\">{{ formatNumber(SelectedSource.ItemCount) }}</div>\n <div class=\"at-detail-stat-label\">Items</div>\n </div>\n <div class=\"at-detail-stat\">\n <div class=\"at-detail-stat-value\">{{ formatNumber(SelectedSource.TagCount) }}</div>\n <div class=\"at-detail-stat-label\">Tags</div>\n </div>\n <div class=\"at-detail-stat\">\n <div class=\"at-detail-stat-value\">{{ SelectedSource.AvgTags }}</div>\n <div class=\"at-detail-stat-label\">Avg Tags</div>\n </div>\n <div class=\"at-detail-stat\">\n <div class=\"at-detail-stat-value\">{{ SelectedSource.LastRunAgo }}</div>\n <div class=\"at-detail-stat-label\">Last Run</div>\n </div>\n <div class=\"at-detail-stat\">\n <div class=\"at-detail-stat-value\" [class.at-kpi-error-value]=\"SelectedSource.ErrorCount > 0\">{{ SelectedSource.ErrorCount }}</div>\n <div class=\"at-detail-stat-label\">Errors</div>\n </div>\n </div>\n </div>\n\n <!-- Content Library (status badges, pagination) -->\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-header-row\">\n <div class=\"at-detail-section-label\">Content Library ({{ FilteredSourceDetailTotal }} of {{ SelectedSource.ItemCount | number }})</div>\n <div class=\"at-detail-section-controls\">\n <select class=\"at-detail-filter-select\" [(ngModel)]=\"SourceDetailStatusFilter\" (ngModelChange)=\"OnSourceDetailStatusFilterChange()\">\n @for (opt of SourceDetailStatusOptions; track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n <button class=\"at-action-btn at-retry-btn\" (click)=\"RetryFailedItems()\" title=\"Re-queue failed items for processing\">\n <i class=\"fa-solid fa-rotate-right\"></i> Retry Failed\n </button>\n </div>\n </div>\n <div class=\"at-detail-content-list\">\n @if (FilteredSourceDetailItems.length === 0) {\n <mj-empty-state Size=\"compact\"\n [Variant]=\"SourceDetailStatusFilter === 'All' ? 'empty' : 'no-results'\"\n [Title]=\"SourceDetailStatusFilter === 'All' ? 'No content items yet.' : 'No items match this filter.'\" />\n }\n @for (ci of FilteredSourceDetailItems; track ci.ID) {\n <div class=\"at-detail-content-item\" (click)=\"OpenContentItemDetail(ci)\">\n <div class=\"at-feed-status-dot\" [class]=\"ci.StatusDot\"></div>\n <span class=\"at-detail-content-item-name\">{{ ci.Name }}</span>\n <span class=\"at-status-badge\" [class]=\"GetStatusBadgeClass(ci.EmbeddingStatus)\">E:{{ ci.EmbeddingStatus }}</span>\n <span class=\"at-status-badge\" [class]=\"GetStatusBadgeClass(ci.TaggingStatus)\">T:{{ ci.TaggingStatus }}</span>\n <span class=\"at-detail-content-item-tags\">{{ ci.TagCount }} tags</span>\n <span class=\"at-detail-content-item-time\">{{ ci.UpdatedAt }}</span>\n </div>\n }\n </div>\n <!-- Pagination -->\n @if (SourceDetailTotalPages > 1) {\n <div class=\"at-detail-pagination\">\n <button class=\"at-page-btn\" [disabled]=\"SourceDetailPage === 0\" (click)=\"SourceDetailPrevPage()\">\n <i class=\"fa-solid fa-chevron-left\"></i> Prev\n </button>\n <span class=\"at-page-info\">Page {{ SourceDetailPage + 1 }} of {{ SourceDetailTotalPages }}</span>\n <button class=\"at-page-btn\" [disabled]=\"SourceDetailPage >= SourceDetailTotalPages - 1\" (click)=\"SourceDetailNextPage()\">\n Next <i class=\"fa-solid fa-chevron-right\"></i>\n </button>\n </div>\n }\n </div>\n\n <!-- Run History -->\n @if (SelectedSource.RunHistory.length > 0) {\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-label\">Recent Runs</div>\n <div class=\"at-detail-run-history\">\n @for (run of SelectedSource.RunHistory; track run.ID) {\n <div class=\"at-detail-run-row\">\n <span class=\"at-run-status-badge\" [class]=\"run.StatusClass\">{{ run.Status }}</span>\n <span class=\"at-detail-run-time\">{{ run.StartedDisplay }}</span>\n <span class=\"at-detail-run-duration\">{{ run.Duration }}</span>\n <span class=\"at-detail-run-items\">{{ run.Items }} items</span>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Actions -->\n <div class=\"at-detail-actions\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"OpenEditSourceFromDetail()\">\n <i class=\"fa-solid fa-pen\"></i> Edit\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"RunSourceFromDetail()\">\n <i class=\"fa-solid fa-play\"></i> Run Now\n </button>\n <button class=\"at-action-btn at-secondary-btn at-source-delete-btn\" (click)=\"DeleteSourceFromDetail()\">\n <i class=\"fa-solid fa-trash\"></i> Delete\n </button>\n </div>\n }\n </div>\n </div>\n}\n\n<!-- \u2550\u2550\u2550\u2550\u2550\u2550 SCHEDULE DIALOG \u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (ShowScheduleDialog && SchedulingSourceCard) {\n <div class=\"at-schedule-overlay\" (click)=\"CloseScheduleDialog()\">\n <div class=\"at-schedule-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"at-schedule-dialog-header\">\n <h3><i class=\"fa-regular fa-clock\"></i> Schedule Pipeline</h3>\n <button class=\"at-schedule-dialog-close\" (click)=\"CloseScheduleDialog()\" aria-label=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"at-schedule-dialog-body\">\n <div class=\"at-schedule-field\">\n <label>Source</label>\n <div class=\"at-schedule-source-name\">\n <i [class]=\"SchedulingSourceCard.Icon\"></i>\n {{ SchedulingSourceCard.Name }}\n </div>\n </div>\n <div class=\"at-schedule-field\">\n <label>Action</label>\n <div class=\"at-schedule-action-name\">Autotag and Vectorize Content</div>\n </div>\n <div class=\"at-schedule-field\">\n <label for=\"schedule-cron\">Cron Expression</label>\n <input id=\"schedule-cron\"\n type=\"text\"\n class=\"mj-input at-schedule-cron-input\"\n [(ngModel)]=\"ScheduleCron\"\n placeholder=\"0 2 * * *\" />\n <div class=\"at-schedule-cron-preview\">\n <i class=\"fa-solid fa-info-circle\"></i>\n {{ GetCronPreview(ScheduleCron) }}\n </div>\n </div>\n <div class=\"at-schedule-field at-schedule-toggle-row\">\n <label for=\"schedule-enabled\">Enabled</label>\n <input id=\"schedule-enabled\"\n type=\"checkbox\"\n class=\"mj-checkbox\"\n [(ngModel)]=\"ScheduleEnabled\" />\n </div>\n </div>\n <div class=\"at-schedule-dialog-footer\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"SaveSchedule()\" [disabled]=\"ScheduleSaving\">\n @if (ScheduleSaving) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Saving...\n } @else {\n <i class=\"fa-solid fa-check\"></i> Create Schedule\n }\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"CloseScheduleDialog()\">Cancel</button>\n </div>\n </div>\n </div>\n}\n\n<!-- \u2550\u2550\u2550\u2550\u2550\u2550 DRY-RUN DISPOSITION PREVIEW (in-memory, nothing persisted) \u2550\u2550\u2550\u2550\u2550\u2550 -->\n<classify-dry-run-preview\n [Provider]=\"Provider\"\n [Show]=\"ShowDryRunPreview\"\n [SourceCard]=\"DryRunSourceCard\"\n (Closed)=\"CloseDryRunPreview()\">\n</classify-dry-run-preview>\n", styles: ["/*\n * Content Sources tab styles.\n *\n * The host (AutotaggingPipelineResourceComponent) uses ViewEncapsulation.None,\n * so its `.at-*` rules are global and already style this tab's markup (cards,\n * source-detail slide-in, schedule dialog) while it renders inside the host\n * shell. Tab-specific rules would live here; none are needed during the\n * host-slim step.\n */\n\n.at-sources-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n text-align: center;\n gap: 10px;\n padding: 48px 24px;\n background: var(--mj-bg-surface-card);\n border: 1px dashed var(--mj-border-default);\n border-radius: 12px;\n}\n.at-sources-empty > i {\n font-size: 2rem;\n color: var(--mj-brand-primary);\n}\n.at-sources-empty h3 {\n margin: 0;\n font-size: 1rem;\n color: var(--mj-text-primary);\n}\n.at-sources-empty p {\n margin: 0;\n max-width: 460px;\n font-size: 0.85rem;\n color: var(--mj-text-muted);\n line-height: 1.5;\n}\n.at-sources-empty-actions {\n display: flex;\n gap: 8px;\n margin-top: 6px;\n}\n"] }]
|
|
1401
|
+
args: [{ standalone: false, selector: 'classify-sources-tab', template: "<mj-page-header-interior\n Title=\"Content Sources\"\n Subtitle=\"Configure where content is ingested from\">\n <div actions>\n <button mjButton variant=\"secondary\" size=\"sm\" (click)=\"Refresh()\" [disabled]=\"IsLoading\">\n @if (IsLoading) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> <span class=\"mj-action-label\">Refreshing\u2026</span>\n } @else {\n <i class=\"fa-solid fa-arrows-rotate\"></i> <span class=\"mj-action-label\">Refresh</span>\n }\n </button>\n <button mjButton variant=\"secondary\" size=\"sm\" (click)=\"onAddSourceGuided()\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i> <span class=\"mj-action-label\">Add Source (Guided)</span>\n </button>\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"onAddSource()\">\n <i class=\"fa-solid fa-plus\"></i> <span class=\"mj-action-label\">Add Source</span>\n </button>\n </div>\n</mj-page-header-interior>\n\n<mj-page-body-interior [Padding]=\"false\" [Flex]=\"true\">\n <div class=\"at-main-area\">\n @if (IsLoading) {\n <div class=\"at-loading-overlay\">\n <mj-loading text=\"Refreshing sources\u2026\"></mj-loading>\n </div>\n }\n <div class=\"at-page-body\">\n @if (SourceCards.length === 0 && !IsLoading) {\n <mj-empty-state class=\"at-sources-empty\" Icon=\"fa-solid fa-database\"\n Title=\"No content sources yet\"\n Message=\"Use the guided setup to stand up your first source \u2014 pick an entity or URL, configure its document, taxonomy, and domain context in one flow.\">\n <div class=\"at-sources-empty-actions\" ngProjectAs=\"[actions]\">\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"onAddSourceGuided()\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i> Add Source (Guided)\n </button>\n <button mjButton variant=\"secondary\" size=\"sm\" (click)=\"onAddSource()\">\n <i class=\"fa-solid fa-plus\"></i> Add manually\n </button>\n </div>\n </mj-empty-state>\n }\n\n <div class=\"at-sources-grid\">\n @for (card of SourceCards; track card.ID) {\n <div class=\"at-source-card-full at-source-card-clickable\" (click)=\"OpenSourceDetail(card)\">\n <div class=\"at-source-card-header\">\n <div class=\"at-source-card-icon\"><i [class]=\"card.Icon\"></i></div>\n <div>\n <div class=\"at-source-card-title\">{{ card.Name }}</div>\n <div class=\"at-source-card-type\">{{ card.SourceTypeName }}@if (card.RequiresFileType) { \u00B7 {{ card.FileTypeName }}}</div>\n </div>\n <div class=\"at-source-card-status\" [class]=\"card.StatusClass\">\n <div class=\"at-source-mini-status\" [class]=\"card.StatusClass\"></div>\n {{ card.StatusLabel }}\n </div>\n </div>\n @if (card.URL) {\n <div class=\"at-source-card-url\">{{ card.URL }}</div>\n }\n <div class=\"at-source-card-stats\">\n <div class=\"at-source-stat\">\n <div class=\"at-source-stat-value\">{{ formatNumber(card.ItemCount) }}</div>\n <div class=\"at-source-stat-label\">Items</div>\n </div>\n <div class=\"at-source-stat\">\n <div class=\"at-source-stat-value\">{{ formatNumber(card.TagCount) }}</div>\n <div class=\"at-source-stat-label\">Tags</div>\n </div>\n <div class=\"at-source-stat\">\n <div class=\"at-source-stat-value\">{{ card.AvgTags }}</div>\n <div class=\"at-source-stat-label\">Avg Tags</div>\n </div>\n <div class=\"at-source-stat\">\n <div class=\"at-source-stat-value\">{{ card.LastRunAgo }}</div>\n <div class=\"at-source-stat-label\">Last Run</div>\n </div>\n </div>\n <!-- Schedule indicator -->\n @if (card.ScheduledJobID) {\n <div class=\"at-schedule-indicator\" (click)=\"RemoveSchedule(card); $event.stopPropagation()\" title=\"Click to remove schedule\">\n <i class=\"fa-regular fa-clock\"></i>\n <span>{{ GetScheduleLabel(card) }}</span>\n </div>\n }\n <div class=\"at-source-card-actions\">\n <button class=\"at-source-action-btn\" (click)=\"onEditSource(card); $event.stopPropagation()\"><i class=\"fa-solid fa-pen\"></i> Edit</button>\n <button class=\"at-source-action-btn\" (click)=\"onRunSource(card.ID); $event.stopPropagation()\"><i class=\"fa-solid fa-play\"></i> Run</button>\n <button class=\"at-source-action-btn\" (click)=\"OpenDryRunPreview(card); $event.stopPropagation()\"><i class=\"fa-solid fa-flask\"></i> Preview</button>\n @if (!card.ScheduledJobID) {\n <button class=\"at-source-action-btn at-source-schedule-btn\" (click)=\"OpenScheduleDialog(card); $event.stopPropagation()\"><i class=\"fa-regular fa-clock\"></i> Schedule</button>\n }\n <button class=\"at-source-action-btn at-source-delete-btn\" (click)=\"DeleteSource(card); $event.stopPropagation()\"><i class=\"fa-solid fa-trash\"></i> Delete</button>\n </div>\n </div>\n }\n\n <!-- Add Source Card -->\n <div class=\"at-add-source-card\" (click)=\"onAddSource()\">\n <i class=\"fa-solid fa-plus-circle\"></i>\n <span style=\"font-size: 0.85rem; font-weight: 600;\">Add Content Source</span>\n <span style=\"font-size: 0.72rem;\">Web, RSS, Email, Files, API</span>\n </div>\n </div>\n </div>\n </div>\n</mj-page-body-interior>\n\n<!-- \u2550\u2550\u2550\u2550\u2550\u2550 SOURCE DETAIL SLIDE-IN \u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (ShowSourceDetail) {\n <div class=\"at-slide-overlay\" (click)=\"CloseSourceDetail()\"></div>\n <div class=\"at-slide-panel at-detail-panel\">\n <div class=\"at-slide-header\">\n <h3><i class=\"fa-solid fa-database\"></i> Source Detail</h3>\n <button class=\"at-slide-close\" aria-label=\"Close source detail\" (click)=\"CloseSourceDetail()\"><i class=\"fa-solid fa-times\"></i></button>\n </div>\n <div class=\"at-slide-body\">\n @if (SourceDetailLoading) {\n <div class=\"at-loading-overlay\">\n <mj-loading text=\"Loading source details...\"></mj-loading>\n </div>\n }\n @if (!SourceDetailLoading && SelectedSource) {\n <!-- Source Header -->\n <div class=\"at-detail-source-header\">\n <div class=\"at-source-card-icon\"><i [class]=\"SelectedSource.Icon\"></i></div>\n <div>\n <h4 class=\"at-detail-item-name\">{{ SelectedSource.Name }}</h4>\n <div class=\"at-detail-badges\">\n <span class=\"at-detail-badge at-detail-badge-type\">{{ SelectedSource.SourceTypeName }}</span>\n <span class=\"at-detail-badge\" [class]=\"'at-detail-badge-status-' + SelectedSource.StatusClass\">\n {{ SelectedSource.StatusLabel }}\n </span>\n </div>\n </div>\n </div>\n\n <!-- Configuration -->\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-label\">Configuration</div>\n <div class=\"at-detail-meta-grid\">\n @if (SelectedSource.URL) {\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">URL</span>\n <a [href]=\"SelectedSource.URL\" target=\"_blank\" class=\"at-detail-link at-detail-meta-value\">{{ SelectedSource.URL }}</a>\n </div>\n }\n @if (SelectedSource.RequiresFileType) {\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">Content Type</span>\n <span class=\"at-detail-meta-value\">{{ SelectedSource.ContentTypeName }}</span>\n </div>\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">File Type</span>\n <span class=\"at-detail-meta-value\">{{ SelectedSource.FileTypeName }}</span>\n </div>\n }\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">Embedding Model</span>\n <span class=\"at-detail-meta-value\">{{ SelectedSource.EmbeddingModelName }}</span>\n </div>\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">Vector Index</span>\n <span class=\"at-detail-meta-value\">{{ SelectedSource.VectorIndexName }}</span>\n </div>\n </div>\n </div>\n\n <!-- Stats -->\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-label\">Statistics</div>\n <div class=\"at-detail-stats-strip\">\n <div class=\"at-detail-stat\">\n <div class=\"at-detail-stat-value\">{{ formatNumber(SelectedSource.ItemCount) }}</div>\n <div class=\"at-detail-stat-label\">Items</div>\n </div>\n <div class=\"at-detail-stat\">\n <div class=\"at-detail-stat-value\">{{ formatNumber(SelectedSource.TagCount) }}</div>\n <div class=\"at-detail-stat-label\">Tags</div>\n </div>\n <div class=\"at-detail-stat\">\n <div class=\"at-detail-stat-value\">{{ SelectedSource.AvgTags }}</div>\n <div class=\"at-detail-stat-label\">Avg Tags</div>\n </div>\n <div class=\"at-detail-stat\">\n <div class=\"at-detail-stat-value\">{{ SelectedSource.LastRunAgo }}</div>\n <div class=\"at-detail-stat-label\">Last Run</div>\n </div>\n <div class=\"at-detail-stat\">\n <div class=\"at-detail-stat-value\" [class.at-kpi-error-value]=\"SelectedSource.ErrorCount > 0\">{{ SelectedSource.ErrorCount }}</div>\n <div class=\"at-detail-stat-label\">Errors</div>\n </div>\n </div>\n </div>\n\n <!-- Content Library (status badges, pagination) -->\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-header-row\">\n <div class=\"at-detail-section-label\">Content Library ({{ FilteredSourceDetailTotal }} of {{ SelectedSource.ItemCount | number }})</div>\n <div class=\"at-detail-section-controls\">\n <select class=\"at-detail-filter-select\" [(ngModel)]=\"SourceDetailStatusFilter\" (ngModelChange)=\"OnSourceDetailStatusFilterChange()\">\n @for (opt of SourceDetailStatusOptions; track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n <button class=\"at-action-btn at-retry-btn\" (click)=\"RetryFailedItems()\" title=\"Re-queue failed items for processing\">\n <i class=\"fa-solid fa-rotate-right\"></i> Retry Failed\n </button>\n </div>\n </div>\n <div class=\"at-detail-content-list\">\n @if (FilteredSourceDetailItems.length === 0) {\n <mj-empty-state Size=\"compact\"\n [Variant]=\"SourceDetailStatusFilter === 'All' ? 'empty' : 'no-results'\"\n [Title]=\"SourceDetailStatusFilter === 'All' ? 'No content items yet.' : 'No items match this filter.'\" />\n }\n @for (ci of FilteredSourceDetailItems; track ci.ID) {\n <div class=\"at-detail-content-item\" (click)=\"OpenContentItemDetail(ci)\">\n <div class=\"at-feed-status-dot\" [class]=\"ci.StatusDot\"></div>\n <span class=\"at-detail-content-item-name\">{{ ci.Name }}</span>\n <span class=\"at-status-badge\" [class]=\"GetStatusBadgeClass(ci.EmbeddingStatus)\">E:{{ ci.EmbeddingStatus }}</span>\n <span class=\"at-status-badge\" [class]=\"GetStatusBadgeClass(ci.TaggingStatus)\">T:{{ ci.TaggingStatus }}</span>\n <span class=\"at-detail-content-item-tags\">{{ ci.TagCount }} tags</span>\n <span class=\"at-detail-content-item-time\">{{ ci.UpdatedAt }}</span>\n </div>\n }\n </div>\n <!-- Pagination -->\n @if (SourceDetailTotalPages > 1) {\n <div class=\"at-detail-pagination\">\n <button class=\"at-page-btn\" [disabled]=\"SourceDetailPage === 0\" (click)=\"SourceDetailPrevPage()\">\n <i class=\"fa-solid fa-chevron-left\"></i> Prev\n </button>\n <span class=\"at-page-info\">Page {{ SourceDetailPage + 1 }} of {{ SourceDetailTotalPages }}</span>\n <button class=\"at-page-btn\" [disabled]=\"SourceDetailPage >= SourceDetailTotalPages - 1\" (click)=\"SourceDetailNextPage()\">\n Next <i class=\"fa-solid fa-chevron-right\"></i>\n </button>\n </div>\n }\n </div>\n\n <!-- Run History -->\n @if (SelectedSource.RunHistory.length > 0) {\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-label\">Recent Runs</div>\n <div class=\"at-detail-run-history\">\n @for (run of SelectedSource.RunHistory; track run.ID) {\n <div class=\"at-detail-run-row\">\n <span class=\"at-run-status-badge\" [class]=\"run.StatusClass\">{{ run.Status }}</span>\n <span class=\"at-detail-run-time\">{{ run.StartedDisplay }}</span>\n <span class=\"at-detail-run-duration\">{{ run.Duration }}</span>\n <span class=\"at-detail-run-items\">{{ run.Items }} items</span>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Actions -->\n <div class=\"at-detail-actions\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"OpenEditSourceFromDetail()\">\n <i class=\"fa-solid fa-pen\"></i> Edit\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"RunSourceFromDetail()\">\n <i class=\"fa-solid fa-play\"></i> Run Now\n </button>\n <button class=\"at-action-btn at-secondary-btn at-source-delete-btn\" (click)=\"DeleteSourceFromDetail()\">\n <i class=\"fa-solid fa-trash\"></i> Delete\n </button>\n </div>\n }\n </div>\n </div>\n}\n\n<!-- \u2550\u2550\u2550\u2550\u2550\u2550 SCHEDULE DIALOG \u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (ShowScheduleDialog && SchedulingSourceCard) {\n <div class=\"at-schedule-overlay\" (click)=\"CloseScheduleDialog()\">\n <div class=\"at-schedule-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"at-schedule-dialog-header\">\n <h3><i class=\"fa-regular fa-clock\"></i> Schedule Pipeline</h3>\n <button class=\"at-schedule-dialog-close\" (click)=\"CloseScheduleDialog()\" aria-label=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"at-schedule-dialog-body\">\n <div class=\"at-schedule-field\">\n <label>Source</label>\n <div class=\"at-schedule-source-name\">\n <i [class]=\"SchedulingSourceCard.Icon\"></i>\n {{ SchedulingSourceCard.Name }}\n </div>\n </div>\n <div class=\"at-schedule-field\">\n <label>Action</label>\n <div class=\"at-schedule-action-name\">Autotag and Vectorize Content</div>\n </div>\n <div class=\"at-schedule-field\">\n <label for=\"schedule-cron\">Cron Expression</label>\n <input id=\"schedule-cron\"\n type=\"text\"\n class=\"mj-input at-schedule-cron-input\"\n [(ngModel)]=\"ScheduleCron\"\n placeholder=\"0 2 * * *\" />\n <div class=\"at-schedule-cron-preview\">\n <i class=\"fa-solid fa-info-circle\"></i>\n {{ GetCronPreview(ScheduleCron) }}\n </div>\n </div>\n <div class=\"at-schedule-field at-schedule-toggle-row\">\n <label for=\"schedule-enabled\">Enabled</label>\n <input id=\"schedule-enabled\"\n type=\"checkbox\"\n class=\"mj-checkbox\"\n [(ngModel)]=\"ScheduleEnabled\" />\n </div>\n </div>\n <div class=\"at-schedule-dialog-footer\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"SaveSchedule()\" [disabled]=\"ScheduleSaving\">\n @if (ScheduleSaving) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Saving...\n } @else {\n <i class=\"fa-solid fa-check\"></i> Create Schedule\n }\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"CloseScheduleDialog()\">Cancel</button>\n </div>\n </div>\n </div>\n}\n\n<!-- \u2550\u2550\u2550\u2550\u2550\u2550 DRY-RUN DISPOSITION PREVIEW (in-memory, nothing persisted) \u2550\u2550\u2550\u2550\u2550\u2550 -->\n<classify-dry-run-preview\n [Provider]=\"Provider\"\n [Show]=\"ShowDryRunPreview\"\n [SourceCard]=\"DryRunSourceCard\"\n (Closed)=\"CloseDryRunPreview()\">\n</classify-dry-run-preview>\n", styles: ["/*\n * Content Sources tab styles.\n *\n * The host (AutotaggingPipelineResourceComponent) uses ViewEncapsulation.None,\n * so its `.at-*` rules are global and already style this tab's markup (cards,\n * source-detail slide-in, schedule dialog) while it renders inside the host\n * shell. Tab-specific rules would live here; none are needed during the\n * host-slim step.\n */\n\n.at-sources-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n text-align: center;\n gap: 10px;\n padding: 48px 24px;\n background: var(--mj-bg-surface-card);\n border: 1px dashed var(--mj-border-default);\n border-radius: 12px;\n}\n.at-sources-empty > i {\n font-size: 2rem;\n color: var(--mj-brand-primary);\n}\n.at-sources-empty h3 {\n margin: 0;\n font-size: 1rem;\n color: var(--mj-text-primary);\n}\n.at-sources-empty p {\n margin: 0;\n max-width: 460px;\n font-size: 0.85rem;\n color: var(--mj-text-muted);\n line-height: 1.5;\n}\n.at-sources-empty-actions {\n display: flex;\n gap: 8px;\n margin-top: 6px;\n}\n"] }]
|
|
1426
1402
|
}], null, { Sources: [{
|
|
1427
1403
|
type: Input
|
|
1428
1404
|
}], Items: [{
|
|
@@ -1435,7 +1411,7 @@ export class ClassifySourcesTabComponent extends BaseAngularComponent {
|
|
|
1435
1411
|
type: Input
|
|
1436
1412
|
}], TotalTagCount: [{
|
|
1437
1413
|
type: Input
|
|
1438
|
-
}],
|
|
1414
|
+
}], ScheduledJobs: [{
|
|
1439
1415
|
type: Input
|
|
1440
1416
|
}], EntityRecordDocCache: [{
|
|
1441
1417
|
type: Input
|
|
@@ -1452,5 +1428,5 @@ export class ClassifySourcesTabComponent extends BaseAngularComponent {
|
|
|
1452
1428
|
}], DataChanged: [{
|
|
1453
1429
|
type: Output
|
|
1454
1430
|
}] }); })();
|
|
1455
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ClassifySourcesTabComponent, { className: "ClassifySourcesTabComponent", filePath: "src/AI/components/autotagging/tabs/sources-tab.component.ts", lineNumber:
|
|
1431
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ClassifySourcesTabComponent, { className: "ClassifySourcesTabComponent", filePath: "src/AI/components/autotagging/tabs/sources-tab.component.ts", lineNumber: 36 }); })();
|
|
1456
1432
|
//# sourceMappingURL=sources-tab.component.js.map
|