@resolveio/client-lib-aicoder-dashboard 21.0.0 → 21.0.3
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as i0 from"@angular/core";import{Injectable,EventEmitter,Output,Input,Component,NgModule}from"@angular/core";import{CommonModule}from"@angular/common";class AICoderDashboardService{buildDefaultSummary(){return{overview:{app_name:"ResolveIO App",app_status:"Active",plan_tier:"small",max_users:10},database:{name:"resolveio",host:"127.0.0.1",status:"Ready"},infrastructure:{current_tier:"small",available_tiers:["small","medium","large"],max_users:10,backend_status:"Ready"},code_changes:{release_notes:"Latest updates are live and available.",last_deployed_at:"",recent_updates:["Overview tab highlights app status and plan tier.","Infrastructure tab shows Small, Medium, and Large options.","Code Changes tab summarizes customer-facing updates."]}}}buildTierOptions(){return[{id:"small",label:"Small",description:"Great for focused teams and lighter usage."},{id:"medium",label:"Medium",description:"Balanced capacity for growing teams and workflows."},{id:"large",label:"Large",description:"Higher capacity for heavy usage and larger teams."}]}static"ɵfac"=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardService,deps:[],target:i0.ɵɵFactoryTarget.Injectable});static"ɵprov"=i0.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardService,providedIn:"root"})}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardService,decorators:[{type:Injectable,args:[{providedIn:"root"}]}]});class AICoderDashboardComponent{dashboardService;summary;loading=!1;errorMessage="";activeTab="overview";tierOptions;tabChange=new EventEmitter;retry=new EventEmitter;tierChange=new EventEmitter;constructor(t){this.dashboardService=t,this.summary=this.dashboardService.buildDefaultSummary(),this.tierOptions=this.dashboardService.buildTierOptions()}setTab(t){this.activeTab!==t&&this.tabChange.emit(t)}requestRetry(){this.retry.emit()}requestTierChange(t){this.tierChange.emit(t)}isCurrentTier(t){return String(this.summary?.infrastructure?.current_tier||"").trim().toLowerCase()===t}static"ɵfac"=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardComponent,deps:[{token:AICoderDashboardService}],target:i0.ɵɵFactoryTarget.Component});static"ɵcmp"=i0.ɵɵngDeclareComponent({minVersion:"17.0.0",version:"21.1.2",type:AICoderDashboardComponent,isStandalone:!1,selector:"resolveio-client-lib-aicoder-dashboard",inputs:{summary:"summary",loading:"loading",errorMessage:"errorMessage",activeTab:"activeTab",tierOptions:"tierOptions"},outputs:{tabChange:"tabChange",retry:"retry",tierChange:"tierChange"},ngImport:i0,template:'<div class="aicoder-dashboard">\n\t<div class="aicoder-dashboard__tabs">\n\t\t<button type="button" [class.is-active]="activeTab === \'overview\'" (click)="setTab(\'overview\')">Overview</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'database\'" (click)="setTab(\'database\')">Database</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'infrastructure\'" (click)="setTab(\'infrastructure\')">Infrastructure</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'code_changes\'" (click)="setTab(\'code_changes\')">Code Changes</button>\n\t</div>\n\n\t@if (loading) {\n\t\t<div class="aicoder-dashboard__state">Loading dashboard details...</div>\n\t} @else if (errorMessage) {\n\t\t<div class="aicoder-dashboard__state aicoder-dashboard__state--error">\n\t\t\t<span>{{ errorMessage }}</span>\n\t\t\t<button type="button" (click)="requestRetry()">Retry</button>\n\t\t</div>\n\t} @else {\n\t\t@if (activeTab === \'overview\') {\n\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t<div class="tile"><span>App</span><strong>{{ summary.overview.app_name }}</strong></div>\n\t\t\t\t<div class="tile"><span>Status</span><strong>{{ summary.overview.app_status }}</strong></div>\n\t\t\t\t<div class="tile"><span>Tier</span><strong>{{ summary.overview.plan_tier }}</strong></div>\n\t\t\t\t<div class="tile"><span>Max users</span><strong>{{ summary.overview.max_users }}</strong></div>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'database\') {\n\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t<div class="tile"><span>Name</span><strong>{{ summary.database.name }}</strong></div>\n\t\t\t\t<div class="tile"><span>Host</span><strong>{{ summary.database.host }}</strong></div>\n\t\t\t\t<div class="tile"><span>Status</span><strong>{{ summary.database.status }}</strong></div>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'infrastructure\') {\n\t\t\t<div class="aicoder-dashboard__tiers">\n\t\t\t\t@for (tier of tierOptions; track tier.id) {\n\t\t\t\t\t<div class="tier" [class.tier--active]="isCurrentTier(tier.id)">\n\t\t\t\t\t\t<h4>{{ tier.label }}</h4>\n\t\t\t\t\t\t<p>{{ tier.description }}</p>\n\t\t\t\t\t\t<button type="button" (click)="requestTierChange(tier.id)">Choose {{ tier.label }}</button>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'code_changes\') {\n\t\t\t<div class="aicoder-dashboard__changes">\n\t\t\t\t<p>{{ summary.code_changes.release_notes }}</p>\n\t\t\t\t<ul>\n\t\t\t\t\t@for (item of summary.code_changes.recent_updates; track item) {\n\t\t\t\t\t\t<li>{{ item }}</li>\n\t\t\t\t\t}\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t}\n\t}\n</div>\n',styles:[".aicoder-dashboard{--aicoder-primary: #0f4c81;font-family:inherit}.aicoder-dashboard__tabs{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:.75rem}.aicoder-dashboard__tabs button{border:1px solid rgba(15,23,42,.2);background:#fff;padding:.45rem .75rem;border-radius:8px;font-weight:600}.aicoder-dashboard__tabs button.is-active{border-color:var(--aicoder-primary);background:#0f4c811a}.aicoder-dashboard__state{padding:.85rem;border-radius:10px;background:#f8fafc}.aicoder-dashboard__state--error{display:flex;justify-content:space-between;align-items:center;gap:.5rem;background:#fff7ed}.aicoder-dashboard__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:.75rem}.tile{padding:.8rem;border:1px solid rgba(15,23,42,.12);border-radius:10px;background:#fff}.tile span{display:block;font-size:.74rem;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:#0f172a9e}.tile strong{display:block;margin-top:.3rem;font-size:1rem}.aicoder-dashboard__tiers{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:.75rem}.tier{padding:.85rem;border-radius:10px;border:1px solid rgba(15,23,42,.12);background:#f8fafc}.tier--active{border-color:var(--aicoder-primary);background:#0f4c8114}.tier h4{margin:0}.tier p{margin:.45rem 0}.aicoder-dashboard__changes ul{margin:0;padding-left:1.1rem}\n"]})}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardComponent,decorators:[{type:Component,args:[{selector:"resolveio-client-lib-aicoder-dashboard",standalone:!1,template:'<div class="aicoder-dashboard">\n\t<div class="aicoder-dashboard__tabs">\n\t\t<button type="button" [class.is-active]="activeTab === \'overview\'" (click)="setTab(\'overview\')">Overview</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'database\'" (click)="setTab(\'database\')">Database</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'infrastructure\'" (click)="setTab(\'infrastructure\')">Infrastructure</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'code_changes\'" (click)="setTab(\'code_changes\')">Code Changes</button>\n\t</div>\n\n\t@if (loading) {\n\t\t<div class="aicoder-dashboard__state">Loading dashboard details...</div>\n\t} @else if (errorMessage) {\n\t\t<div class="aicoder-dashboard__state aicoder-dashboard__state--error">\n\t\t\t<span>{{ errorMessage }}</span>\n\t\t\t<button type="button" (click)="requestRetry()">Retry</button>\n\t\t</div>\n\t} @else {\n\t\t@if (activeTab === \'overview\') {\n\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t<div class="tile"><span>App</span><strong>{{ summary.overview.app_name }}</strong></div>\n\t\t\t\t<div class="tile"><span>Status</span><strong>{{ summary.overview.app_status }}</strong></div>\n\t\t\t\t<div class="tile"><span>Tier</span><strong>{{ summary.overview.plan_tier }}</strong></div>\n\t\t\t\t<div class="tile"><span>Max users</span><strong>{{ summary.overview.max_users }}</strong></div>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'database\') {\n\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t<div class="tile"><span>Name</span><strong>{{ summary.database.name }}</strong></div>\n\t\t\t\t<div class="tile"><span>Host</span><strong>{{ summary.database.host }}</strong></div>\n\t\t\t\t<div class="tile"><span>Status</span><strong>{{ summary.database.status }}</strong></div>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'infrastructure\') {\n\t\t\t<div class="aicoder-dashboard__tiers">\n\t\t\t\t@for (tier of tierOptions; track tier.id) {\n\t\t\t\t\t<div class="tier" [class.tier--active]="isCurrentTier(tier.id)">\n\t\t\t\t\t\t<h4>{{ tier.label }}</h4>\n\t\t\t\t\t\t<p>{{ tier.description }}</p>\n\t\t\t\t\t\t<button type="button" (click)="requestTierChange(tier.id)">Choose {{ tier.label }}</button>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'code_changes\') {\n\t\t\t<div class="aicoder-dashboard__changes">\n\t\t\t\t<p>{{ summary.code_changes.release_notes }}</p>\n\t\t\t\t<ul>\n\t\t\t\t\t@for (item of summary.code_changes.recent_updates; track item) {\n\t\t\t\t\t\t<li>{{ item }}</li>\n\t\t\t\t\t}\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t}\n\t}\n</div>\n',styles:[".aicoder-dashboard{--aicoder-primary: #0f4c81;font-family:inherit}.aicoder-dashboard__tabs{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:.75rem}.aicoder-dashboard__tabs button{border:1px solid rgba(15,23,42,.2);background:#fff;padding:.45rem .75rem;border-radius:8px;font-weight:600}.aicoder-dashboard__tabs button.is-active{border-color:var(--aicoder-primary);background:#0f4c811a}.aicoder-dashboard__state{padding:.85rem;border-radius:10px;background:#f8fafc}.aicoder-dashboard__state--error{display:flex;justify-content:space-between;align-items:center;gap:.5rem;background:#fff7ed}.aicoder-dashboard__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:.75rem}.tile{padding:.8rem;border:1px solid rgba(15,23,42,.12);border-radius:10px;background:#fff}.tile span{display:block;font-size:.74rem;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:#0f172a9e}.tile strong{display:block;margin-top:.3rem;font-size:1rem}.aicoder-dashboard__tiers{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:.75rem}.tier{padding:.85rem;border-radius:10px;border:1px solid rgba(15,23,42,.12);background:#f8fafc}.tier--active{border-color:var(--aicoder-primary);background:#0f4c8114}.tier h4{margin:0}.tier p{margin:.45rem 0}.aicoder-dashboard__changes ul{margin:0;padding-left:1.1rem}\n"]}]}],ctorParameters:()=>[{type:AICoderDashboardService}],propDecorators:{summary:[{type:Input}],loading:[{type:Input}],errorMessage:[{type:Input}],activeTab:[{type:Input}],tierOptions:[{type:Input}],tabChange:[{type:Output}],retry:[{type:Output}],tierChange:[{type:Output}]}});class AICoderDashboardModule{static"ɵfac"=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardModule,deps:[],target:i0.ɵɵFactoryTarget.NgModule});static"ɵmod"=i0.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardModule,declarations:[AICoderDashboardComponent],imports:[CommonModule],exports:[AICoderDashboardComponent]});static"ɵinj"=i0.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardModule,imports:[CommonModule]})}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardModule,decorators:[{type:NgModule,args:[{imports:[CommonModule],declarations:[AICoderDashboardComponent],exports:[AICoderDashboardComponent]}]}]});export{AICoderDashboardComponent,AICoderDashboardModule,AICoderDashboardService};
|
|
1
|
+
import*as i0 from"@angular/core";import{Injectable,EventEmitter,Output,Input,Component,NgModule}from"@angular/core";import*as i1 from"@angular/common/http";import{HttpHeaders,HttpClientModule}from"@angular/common/http";import*as i2 from"@resolveio/client-lib-core";import{Observable,throwError,interval}from"rxjs";import{switchMap,map,tap,shareReplay}from"rxjs/operators";import*as i2$1 from"@angular/forms";import{FormsModule}from"@angular/forms";import{CommonModule}from"@angular/common";class AICoderDashboardService{buildDefaultSummary(){return{overview:{app_name:"ResolveIO App",app_status:"Active",plan_tier:"small",max_users:10},database:{name:"resolveio",host:"127.0.0.1",status:"Ready"},infrastructure:{current_tier:"small",available_tiers:["small","medium","large"],max_users:10,backend_status:"Ready"},code_changes:{release_notes:"Latest updates are live and available.",last_deployed_at:"",recent_updates:["Overview tab highlights app status and plan tier.","Infrastructure tab shows Small, Medium, and Large options.","Code Changes tab summarizes customer-facing updates."]},subscription:{current_package:"Small",current_tier:"small",package_price_label:"$99 / month",billing_status:"Paid",billing_mode:"Subscription",subscription_status:"active",current_period_end:"",manage_url:"",can_downgrade:!0,downgrade_reason:""}}}buildTierOptions(){return[{id:"small",label:"Small",description:"Great for focused teams and lighter usage."},{id:"medium",label:"Medium",description:"Balanced capacity for growing teams and workflows."},{id:"large",label:"Large",description:"Higher capacity for heavy usage and larger teams."}]}static"ɵfac"=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardService,deps:[],target:i0.ɵɵFactoryTarget.Injectable});static"ɵprov"=i0.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardService,providedIn:"root"})}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardService,decorators:[{type:Injectable,args:[{providedIn:"root"}]}]});class AICoderDashboardComponent{dashboardService;summary;loading=!1;errorMessage="";activeTab="overview";tierOptions;tabChange=new EventEmitter;retry=new EventEmitter;tierChange=new EventEmitter;manageSubscription=new EventEmitter;constructor(t){this.dashboardService=t,this.summary=this.dashboardService.buildDefaultSummary(),this.tierOptions=this.dashboardService.buildTierOptions()}setTab(t){this.activeTab!==t&&this.tabChange.emit(t)}requestRetry(){this.retry.emit()}requestTierChange(t){this.tierChange.emit(t)}requestManageSubscription(){this.manageSubscription.emit(this.resolveManageSubscriptionUrl())}isCurrentTier(t){return String(this.summary?.infrastructure?.current_tier||"").trim().toLowerCase()===t}hasManageSubscriptionUrl(){return!!this.resolveManageSubscriptionUrl()}resolveManageSubscriptionUrl(){return String(this.summary?.subscription?.manage_url||"").trim()}static"ɵfac"=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardComponent,deps:[{token:AICoderDashboardService}],target:i0.ɵɵFactoryTarget.Component});static"ɵcmp"=i0.ɵɵngDeclareComponent({minVersion:"17.0.0",version:"21.1.2",type:AICoderDashboardComponent,isStandalone:!1,selector:"resolveio-client-lib-aicoder-dashboard",inputs:{summary:"summary",loading:"loading",errorMessage:"errorMessage",activeTab:"activeTab",tierOptions:"tierOptions"},outputs:{tabChange:"tabChange",retry:"retry",tierChange:"tierChange",manageSubscription:"manageSubscription"},ngImport:i0,template:'<div class="aicoder-dashboard">\n\t<div class="aicoder-dashboard__tabs">\n\t\t<button type="button" [class.is-active]="activeTab === \'overview\'" (click)="setTab(\'overview\')">Overview</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'database\'" (click)="setTab(\'database\')">Database</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'infrastructure\'" (click)="setTab(\'infrastructure\')">Infrastructure</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'code_changes\'" (click)="setTab(\'code_changes\')">Code Changes</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'subscription\'" (click)="setTab(\'subscription\')">Subscription</button>\n\t</div>\n\n\t@if (loading) {\n\t\t<div class="aicoder-dashboard__state">Loading dashboard details...</div>\n\t} @else if (errorMessage) {\n\t\t<div class="aicoder-dashboard__state aicoder-dashboard__state--error">\n\t\t\t<span>{{ errorMessage }}</span>\n\t\t\t<button type="button" (click)="requestRetry()">Retry</button>\n\t\t</div>\n\t} @else {\n\t\t@if (activeTab === \'overview\') {\n\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t<div class="tile"><span>App</span><strong>{{ summary.overview.app_name }}</strong></div>\n\t\t\t\t<div class="tile"><span>Status</span><strong>{{ summary.overview.app_status }}</strong></div>\n\t\t\t\t<div class="tile"><span>Tier</span><strong>{{ summary.overview.plan_tier }}</strong></div>\n\t\t\t\t<div class="tile"><span>Max users</span><strong>{{ summary.overview.max_users }}</strong></div>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'database\') {\n\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t<div class="tile"><span>Name</span><strong>{{ summary.database.name }}</strong></div>\n\t\t\t\t<div class="tile"><span>Host</span><strong>{{ summary.database.host }}</strong></div>\n\t\t\t\t<div class="tile"><span>Status</span><strong>{{ summary.database.status }}</strong></div>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'infrastructure\') {\n\t\t\t<div class="aicoder-dashboard__tiers">\n\t\t\t\t@for (tier of tierOptions; track tier.id) {\n\t\t\t\t\t<div class="tier" [class.tier--active]="isCurrentTier(tier.id)">\n\t\t\t\t\t\t<h4>{{ tier.label }}</h4>\n\t\t\t\t\t\t<p>{{ tier.description }}</p>\n\t\t\t\t\t\t<button type="button" (click)="requestTierChange(tier.id)">Choose {{ tier.label }}</button>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'code_changes\') {\n\t\t\t<div class="aicoder-dashboard__changes">\n\t\t\t\t<p>{{ summary.code_changes.release_notes }}</p>\n\t\t\t\t<ul>\n\t\t\t\t\t@for (item of summary.code_changes.recent_updates; track item) {\n\t\t\t\t\t\t<li>{{ item }}</li>\n\t\t\t\t\t}\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'subscription\') {\n\t\t\t<div class="aicoder-dashboard__subscription">\n\t\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t\t<div class="tile"><span>Package</span><strong>{{ summary.subscription?.current_package || \'N/A\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Tier</span><strong>{{ summary.subscription?.current_tier || summary.overview.plan_tier || \'N/A\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Price</span><strong>{{ summary.subscription?.package_price_label || \'N/A\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Billing status</span><strong>{{ summary.subscription?.billing_status || \'Unknown\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Billing mode</span><strong>{{ summary.subscription?.billing_mode || \'Unknown\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Subscription</span><strong>{{ summary.subscription?.subscription_status || \'N/A\' }}</strong></div>\n\t\t\t\t</div>\n\t\t\t\t@if (summary.subscription?.current_period_end) {\n\t\t\t\t\t<p class="aicoder-dashboard__hint">Current period ends: {{ summary.subscription?.current_period_end }}</p>\n\t\t\t\t}\n\t\t\t\t@if (summary.subscription?.downgrade_reason) {\n\t\t\t\t\t<p class="aicoder-dashboard__hint aicoder-dashboard__hint--warning">{{ summary.subscription?.downgrade_reason }}</p>\n\t\t\t\t}\n\t\t\t\t<div class="aicoder-dashboard__subscription-actions">\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype="button"\n\t\t\t\t\t\tclass="aicoder-dashboard__primary-action"\n\t\t\t\t\t\t(click)="requestManageSubscription()"\n\t\t\t\t\t\t[disabled]="!hasManageSubscriptionUrl()"\n\t\t\t\t\t>\n\t\t\t\t\t\tUpgrade Or Manage Plan\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t}\n\t}\n</div>\n',styles:[".aicoder-dashboard{--aicoder-primary: #0f4c81;font-family:inherit}.aicoder-dashboard__tabs{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:.75rem}.aicoder-dashboard__tabs button{border:1px solid rgba(15,23,42,.2);background:#fff;padding:.45rem .75rem;border-radius:8px;font-weight:600}.aicoder-dashboard__tabs button.is-active{border-color:var(--aicoder-primary);background:#0f4c811a}.aicoder-dashboard__state{padding:.85rem;border-radius:10px;background:#f8fafc}.aicoder-dashboard__state--error{display:flex;justify-content:space-between;align-items:center;gap:.5rem;background:#fff7ed}.aicoder-dashboard__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:.75rem}.tile{padding:.8rem;border:1px solid rgba(15,23,42,.12);border-radius:10px;background:#fff}.tile span{display:block;font-size:.74rem;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:#0f172a9e}.tile strong{display:block;margin-top:.3rem;font-size:1rem}.aicoder-dashboard__tiers{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:.75rem}.tier{padding:.85rem;border-radius:10px;border:1px solid rgba(15,23,42,.12);background:#f8fafc}.tier--active{border-color:var(--aicoder-primary);background:#0f4c8114}.tier h4{margin:0}.tier p{margin:.45rem 0}.aicoder-dashboard__changes ul{margin:0;padding-left:1.1rem}.aicoder-dashboard__subscription{display:grid;gap:.75rem}.aicoder-dashboard__subscription-actions{display:flex;flex-wrap:wrap;gap:.5rem}.aicoder-dashboard__primary-action{border:1px solid var(--aicoder-primary);background:var(--aicoder-primary);color:#fff;padding:.5rem .85rem;border-radius:8px;font-weight:700}.aicoder-dashboard__primary-action[disabled]{opacity:.55;cursor:not-allowed}.aicoder-dashboard__hint{margin:0;color:#334155;font-size:.88rem}.aicoder-dashboard__hint--warning{color:#9a3412}\n"]})}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardComponent,decorators:[{type:Component,args:[{selector:"resolveio-client-lib-aicoder-dashboard",standalone:!1,template:'<div class="aicoder-dashboard">\n\t<div class="aicoder-dashboard__tabs">\n\t\t<button type="button" [class.is-active]="activeTab === \'overview\'" (click)="setTab(\'overview\')">Overview</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'database\'" (click)="setTab(\'database\')">Database</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'infrastructure\'" (click)="setTab(\'infrastructure\')">Infrastructure</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'code_changes\'" (click)="setTab(\'code_changes\')">Code Changes</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'subscription\'" (click)="setTab(\'subscription\')">Subscription</button>\n\t</div>\n\n\t@if (loading) {\n\t\t<div class="aicoder-dashboard__state">Loading dashboard details...</div>\n\t} @else if (errorMessage) {\n\t\t<div class="aicoder-dashboard__state aicoder-dashboard__state--error">\n\t\t\t<span>{{ errorMessage }}</span>\n\t\t\t<button type="button" (click)="requestRetry()">Retry</button>\n\t\t</div>\n\t} @else {\n\t\t@if (activeTab === \'overview\') {\n\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t<div class="tile"><span>App</span><strong>{{ summary.overview.app_name }}</strong></div>\n\t\t\t\t<div class="tile"><span>Status</span><strong>{{ summary.overview.app_status }}</strong></div>\n\t\t\t\t<div class="tile"><span>Tier</span><strong>{{ summary.overview.plan_tier }}</strong></div>\n\t\t\t\t<div class="tile"><span>Max users</span><strong>{{ summary.overview.max_users }}</strong></div>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'database\') {\n\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t<div class="tile"><span>Name</span><strong>{{ summary.database.name }}</strong></div>\n\t\t\t\t<div class="tile"><span>Host</span><strong>{{ summary.database.host }}</strong></div>\n\t\t\t\t<div class="tile"><span>Status</span><strong>{{ summary.database.status }}</strong></div>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'infrastructure\') {\n\t\t\t<div class="aicoder-dashboard__tiers">\n\t\t\t\t@for (tier of tierOptions; track tier.id) {\n\t\t\t\t\t<div class="tier" [class.tier--active]="isCurrentTier(tier.id)">\n\t\t\t\t\t\t<h4>{{ tier.label }}</h4>\n\t\t\t\t\t\t<p>{{ tier.description }}</p>\n\t\t\t\t\t\t<button type="button" (click)="requestTierChange(tier.id)">Choose {{ tier.label }}</button>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'code_changes\') {\n\t\t\t<div class="aicoder-dashboard__changes">\n\t\t\t\t<p>{{ summary.code_changes.release_notes }}</p>\n\t\t\t\t<ul>\n\t\t\t\t\t@for (item of summary.code_changes.recent_updates; track item) {\n\t\t\t\t\t\t<li>{{ item }}</li>\n\t\t\t\t\t}\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'subscription\') {\n\t\t\t<div class="aicoder-dashboard__subscription">\n\t\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t\t<div class="tile"><span>Package</span><strong>{{ summary.subscription?.current_package || \'N/A\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Tier</span><strong>{{ summary.subscription?.current_tier || summary.overview.plan_tier || \'N/A\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Price</span><strong>{{ summary.subscription?.package_price_label || \'N/A\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Billing status</span><strong>{{ summary.subscription?.billing_status || \'Unknown\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Billing mode</span><strong>{{ summary.subscription?.billing_mode || \'Unknown\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Subscription</span><strong>{{ summary.subscription?.subscription_status || \'N/A\' }}</strong></div>\n\t\t\t\t</div>\n\t\t\t\t@if (summary.subscription?.current_period_end) {\n\t\t\t\t\t<p class="aicoder-dashboard__hint">Current period ends: {{ summary.subscription?.current_period_end }}</p>\n\t\t\t\t}\n\t\t\t\t@if (summary.subscription?.downgrade_reason) {\n\t\t\t\t\t<p class="aicoder-dashboard__hint aicoder-dashboard__hint--warning">{{ summary.subscription?.downgrade_reason }}</p>\n\t\t\t\t}\n\t\t\t\t<div class="aicoder-dashboard__subscription-actions">\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype="button"\n\t\t\t\t\t\tclass="aicoder-dashboard__primary-action"\n\t\t\t\t\t\t(click)="requestManageSubscription()"\n\t\t\t\t\t\t[disabled]="!hasManageSubscriptionUrl()"\n\t\t\t\t\t>\n\t\t\t\t\t\tUpgrade Or Manage Plan\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t}\n\t}\n</div>\n',styles:[".aicoder-dashboard{--aicoder-primary: #0f4c81;font-family:inherit}.aicoder-dashboard__tabs{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:.75rem}.aicoder-dashboard__tabs button{border:1px solid rgba(15,23,42,.2);background:#fff;padding:.45rem .75rem;border-radius:8px;font-weight:600}.aicoder-dashboard__tabs button.is-active{border-color:var(--aicoder-primary);background:#0f4c811a}.aicoder-dashboard__state{padding:.85rem;border-radius:10px;background:#f8fafc}.aicoder-dashboard__state--error{display:flex;justify-content:space-between;align-items:center;gap:.5rem;background:#fff7ed}.aicoder-dashboard__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:.75rem}.tile{padding:.8rem;border:1px solid rgba(15,23,42,.12);border-radius:10px;background:#fff}.tile span{display:block;font-size:.74rem;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:#0f172a9e}.tile strong{display:block;margin-top:.3rem;font-size:1rem}.aicoder-dashboard__tiers{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:.75rem}.tier{padding:.85rem;border-radius:10px;border:1px solid rgba(15,23,42,.12);background:#f8fafc}.tier--active{border-color:var(--aicoder-primary);background:#0f4c8114}.tier h4{margin:0}.tier p{margin:.45rem 0}.aicoder-dashboard__changes ul{margin:0;padding-left:1.1rem}.aicoder-dashboard__subscription{display:grid;gap:.75rem}.aicoder-dashboard__subscription-actions{display:flex;flex-wrap:wrap;gap:.5rem}.aicoder-dashboard__primary-action{border:1px solid var(--aicoder-primary);background:var(--aicoder-primary);color:#fff;padding:.5rem .85rem;border-radius:8px;font-weight:700}.aicoder-dashboard__primary-action[disabled]{opacity:.55;cursor:not-allowed}.aicoder-dashboard__hint{margin:0;color:#334155;font-size:.88rem}.aicoder-dashboard__hint--warning{color:#9a3412}\n"]}]}],ctorParameters:()=>[{type:AICoderDashboardService}],propDecorators:{summary:[{type:Input}],loading:[{type:Input}],errorMessage:[{type:Input}],activeTab:[{type:Input}],tierOptions:[{type:Input}],tabChange:[{type:Output}],retry:[{type:Output}],tierChange:[{type:Output}],manageSubscription:[{type:Output}]}});class AICoderWorkbenchApiService{http;tokenManager;sessionHeaderName="X-AI-Coder-Session";sessionRefreshSkewMs=6e4;defaultSessionTtlMs=144e5;sessionByApiBase=new Map;exchangeByApiBase=new Map;constructor(t,e){this.http=t,this.tokenManager=e}get(t,e){return this.authHeaders(t).pipe(switchMap(a=>this.http.get(this.buildUrl(t,e),{headers:a})))}post(t,e,a={}){return this.authHeaders(t).pipe(switchMap(o=>this.http.post(this.buildUrl(t,e),a,{headers:o})))}patch(t,e,a={}){return this.authHeaders(t).pipe(switchMap(o=>this.http.patch(this.buildUrl(t,e),a,{headers:o})))}resolveDefaultApiBase(){const t=String(window?.location?.hostname||"").trim().toLowerCase();return"aicoder.resolveio.com"===t||t.endsWith(".aicoder.resolveio.com")?"https://backend.aicoder.resolveio.com":""}authHeaders(t){return this.ensureSessionToken(t).pipe(map(t=>new HttpHeaders({[this.sessionHeaderName]:t})))}ensureSessionToken(t){const e=this.normalizeApiBase(t),a=this.sessionByApiBase.get(e);if(a&&a.token&&a.expiresAtMs>Date.now()+this.sessionRefreshSkewMs)return new Observable(t=>{t.next(a.token),t.complete()});const o=this.exchangeByApiBase.get(e);if(o)return o;const r=String(this.tokenManager.getToken("accessToken")||"").trim();if(!r)return throwError(()=>new Error("Sign in before using AICoder."));const i=new HttpHeaders({Authorization:`Bearer ${r}`}),s=this.http.post(this.buildUrl(e,"/api/ai-coder/auth/session"),{},{headers:i}).pipe(map(t=>{const a=String(t?.token||"").trim();if(!a)throw new Error("AICoder session response did not include a token.");return this.sessionByApiBase.set(e,{token:a,expiresAtMs:this.resolveSessionExpiresAtMs(t)}),a}),tap({error:()=>this.sessionByApiBase.delete(e),complete:()=>this.exchangeByApiBase.delete(e)}),shareReplay(1));return this.exchangeByApiBase.set(e,s),s}resolveSessionExpiresAtMs(t){const e=t?.expires_at?new Date(t.expires_at).getTime():0;if(Number.isFinite(e)&&e>Date.now())return e;const a=1e3*Number(t?.expires_in_seconds||0);return Date.now()+(a>0?a:this.defaultSessionTtlMs)}buildUrl(t,e){const a=this.normalizeApiBase(t),o=String(e||"").trim();return a?`${a}${o.startsWith("/")?o:`/${o}`}`:o}normalizeApiBase(t){return String(t||"").trim().replace(/\/+$/,"")}static"ɵfac"=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderWorkbenchApiService,deps:[{token:i1.HttpClient},{token:i2.TokenManagerService}],target:i0.ɵɵFactoryTarget.Injectable});static"ɵprov"=i0.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderWorkbenchApiService,providedIn:"root"})}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderWorkbenchApiService,decorators:[{type:Injectable,args:[{providedIn:"root"}]}],ctorParameters:()=>[{type:i1.HttpClient},{type:i2.TokenManagerService}]});class AICoderWorkbenchComponent{api;appId="";appName="AICoder App";apiBase="";defaultTab="cases";compact=!1;showHeader=!0;autoRefresh=!0;activeTab="cases";jobs=[];selectedJob=null;jobLogs=[];deployOperations=[];deployJobs=[];gitStatus=null;isLoading=!1;isSubmitting=!1;errorMessage="";successMessage="";requestTitle="";requestMessage="";chatMessage="";workflowMode="qa_playbook";requestKind="bug_or_feature";dataChangePolicy="review_before_write";deploymentTarget="branch_pr";evidenceNotes="";qaFocus="";riskNotes="";refreshSub;tabs=[{id:"cases",label:"Cases"},{id:"chat",label:"Chat + PR"},{id:"qa",label:"QA Playbook"},{id:"deploy",label:"Deploy"},{id:"git",label:"Git"}];constructor(t){this.api=t}ngOnInit(){this.activeTab=this.defaultTab||"cases",this.apiBase||(this.apiBase=this.api.resolveDefaultApiBase()),this.refresh(),this.startAutoRefresh()}ngOnChanges(t){t.defaultTab&&this.defaultTab&&(this.activeTab=this.defaultTab),(t.appId&&!t.appId.firstChange||t.apiBase&&!t.apiBase.firstChange)&&this.refresh()}ngOnDestroy(){this.refreshSub?.unsubscribe()}setTab(t){this.activeTab=t,"git"!==t||this.gitStatus||this.loadGitStatus(),"deploy"!==t||this.deployOperations.length||this.loadDeployHistory()}refresh(){this.normalizedAppId?(this.loadJobs(),this.loadDeployHistory(),this.loadGitStatus()):this.errorMessage="AICoder app id is missing."}selectJob(t){this.selectedJob=t,this.loadJobLogs(t._id)}submitRequest(){if(!this.normalizedAppId||this.isSubmitting)return;const t=String(this.requestMessage||"").trim();t?(this.isSubmitting=!0,this.clearMessages(),this.api.post(this.apiBase,`/api/ai-coder/apps/${this.normalizedAppId}/requests`,{title:String(this.requestTitle||"").trim(),message:t,workflow_mode:this.workflowMode,request_kind:this.requestKind,data_change_policy:this.dataChangePolicy,deployment_target:this.deploymentTarget,evidence_notes:this.evidenceNotes,qa_focus:this.qaFocus,risk_notes:this.riskNotes}).subscribe({next:t=>{this.successMessage="AICoder case queued.",this.requestTitle="",this.requestMessage="",t?.job&&(this.jobs=[t.job,...this.jobs.filter(e=>e._id!==t.job._id)],this.selectJob(t.job))},error:t=>this.handleError(t,"Could not queue the AICoder case."),complete:()=>this.isSubmitting=!1})):this.errorMessage="Describe the requested change before queueing it."}sendChatMessage(){const t=this.selectedJobId,e=String(this.chatMessage||"").trim();t&&e&&!this.isSubmitting&&(this.isSubmitting=!0,this.clearMessages(),this.api.post(this.apiBase,`/api/ai-coder/jobs/${t}/prompt`,{message:e}).subscribe({next:t=>{this.successMessage="Message sent to AICoder.",this.chatMessage="",t?.job&&(this.mergeJob(t.job),this.selectJob(t.job))},error:t=>this.handleError(t,"Could not send the message."),complete:()=>this.isSubmitting=!1}))}approveSelectedJob(){this.runJobAction("approve","Case approved.")}createPullRequest(){this.runJobAction("create-pr","Pull request requested.")}publishSelectedJob(){this.runJobAction("publish","Publish requested.")}queueDeploy(t){this.normalizedAppId&&!this.isSubmitting&&(this.isSubmitting=!0,this.clearMessages(),this.api.post(this.apiBase,`/api/ai-coder/apps/${this.normalizedAppId}/deploy`,{deploy_client:"full"===t||"client"===t,deploy_backend:"full"===t||"backend"===t,clear_destination:!0,invalidate:!0}).subscribe({next:()=>{this.successMessage="full"===t?"Full deploy queued.":("client"===t?"Client":"Server")+" deploy queued.",this.loadDeployHistory()},error:t=>this.handleError(t,"Could not queue deploy."),complete:()=>this.isSubmitting=!1}))}openPr(t=this.selectedJob){const e=this.resolvePrUrl(t);e&&window.open(e,"_blank","noopener")}loadJobs(){this.normalizedAppId&&(this.isLoading=!0,this.api.get(this.apiBase,`/api/ai-coder/apps/${this.normalizedAppId}/jobs`).subscribe({next:t=>{this.jobs=(t?.jobs||[]).map(t=>this.normalizeJob(t)),!this.selectedJob&&this.jobs.length&&this.selectJob(this.jobs[0])},error:t=>this.handleError(t,"Could not load AICoder cases."),complete:()=>this.isLoading=!1}))}loadJobLogs(t){const e=String(t||"").trim();e?this.api.get(this.apiBase,`/api/ai-coder/jobs/${e}/logs?limit=80`).subscribe({next:t=>this.jobLogs=t?.logs||[],error:t=>this.handleError(t,"Could not load case logs.")}):this.jobLogs=[]}loadDeployHistory(){this.normalizedAppId&&this.api.get(this.apiBase,`/api/ai-coder/apps/${this.normalizedAppId}/deploy/history`).subscribe({next:t=>{this.deployOperations=t?.operations||[],this.deployJobs=t?.jobs||t?.deploy_jobs||[]},error:t=>this.handleError(t,"Could not load deploy history.")})}loadGitStatus(){this.normalizedAppId&&this.api.get(this.apiBase,`/api/ai-coder/apps/${this.normalizedAppId}/git`).subscribe({next:t=>this.gitStatus=t?.status||t||null,error:t=>this.handleError(t,"Could not load git status.")})}get normalizedAppId(){return String(this.appId||"").trim()}get selectedJobId(){return String(this.selectedJob?._id||"").trim()}get activeDeploys(){return this.deployOperations.filter(t=>"In Progress"===String(t?.status||"").trim())}get recentDeploys(){return this.deployOperations.filter(t=>"In Progress"!==String(t?.status||"").trim()).slice(0,6)}isActiveJob(t){const e=String(t?.status||"").toLowerCase();return"queued"===e||"in progress"===e||"running"===e||"pending"===e}resolvePrUrl(t){return String(t?.pr_url||t?.pull_request_url||"").trim()}formatDate(t){if(!t)return"Not recorded";const e=t instanceof Date?t:new Date(t);return Number.isNaN(e.getTime())?String(t):e.toLocaleString()}runJobAction(t,e){const a=this.selectedJobId;a&&!this.isSubmitting&&(this.isSubmitting=!0,this.clearMessages(),this.api.post(this.apiBase,`/api/ai-coder/jobs/${a}/${t}`,{}).subscribe({next:t=>{this.successMessage=e,t?.job&&(this.mergeJob(t.job),this.selectJob(t.job))},error:e=>this.handleError(e,`Could not ${t.replace("-"," ")}.`),complete:()=>this.isSubmitting=!1}))}mergeJob(t){const e=this.normalizeJob(t);this.jobs=[e,...this.jobs.filter(t=>t._id!==e._id)],this.selectedJob=e}normalizeJob(t){return{...t,_id:String(t?._id||"").trim(),title:String(t?.title||"AICoder Case").trim(),status:String(t?.status||"").trim()||"Queued"}}startAutoRefresh(){this.refreshSub?.unsubscribe(),this.autoRefresh&&(this.refreshSub=interval(15e3).subscribe(()=>{(this.jobs.some(t=>this.isActiveJob(t))||this.activeDeploys.length)&&this.refresh()}))}clearMessages(){this.errorMessage="",this.successMessage=""}handleError(t,e){this.errorMessage=String(t?.error?.message||t?.message||e),this.successMessage="",this.isSubmitting=!1,this.isLoading=!1}static"ɵfac"=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderWorkbenchComponent,deps:[{token:AICoderWorkbenchApiService}],target:i0.ɵɵFactoryTarget.Component});static"ɵcmp"=i0.ɵɵngDeclareComponent({minVersion:"17.0.0",version:"21.1.2",type:AICoderWorkbenchComponent,isStandalone:!1,selector:"resolveio-client-lib-aicoder-workbench",inputs:{appId:"appId",appName:"appName",apiBase:"apiBase",defaultTab:"defaultTab",compact:"compact",showHeader:"showHeader",autoRefresh:"autoRefresh"},usesOnChanges:!0,ngImport:i0,template:'<section class="aicoder-workbench" [class.aicoder-workbench--compact]="compact">\n\t@if (showHeader) {\n\t\t<header class="aicoder-workbench__header">\n\t\t\t<div>\n\t\t\t\t<p>AICoder</p>\n\t\t\t\t<h2>{{ appName || \'AICoder App\' }}</h2>\n\t\t\t</div>\n\t\t\t<button type="button" class="aicoder-workbench__ghost" (click)="refresh()" [disabled]="isLoading || isSubmitting">Refresh</button>\n\t\t</header>\n\t}\n\n\t<nav class="aicoder-workbench__tabs" aria-label="AICoder workbench tabs">\n\t\t@for (tab of tabs; track tab.id) {\n\t\t\t<button type="button" [class.is-active]="activeTab === tab.id" (click)="setTab(tab.id)">\n\t\t\t\t{{ tab.label }}\n\t\t\t</button>\n\t\t}\n\t</nav>\n\n\t@if (errorMessage) {\n\t\t<div class="aicoder-workbench__notice aicoder-workbench__notice--error">{{ errorMessage }}</div>\n\t}\n\t@if (successMessage) {\n\t\t<div class="aicoder-workbench__notice aicoder-workbench__notice--success">{{ successMessage }}</div>\n\t}\n\n\t@if (activeTab === \'cases\') {\n\t\t<div class="aicoder-workbench__layout">\n\t\t\t<section class="aicoder-workbench__panel">\n\t\t\t\t<h3>New Case</h3>\n\t\t\t\t<input class="aicoder-workbench__input" [(ngModel)]="requestTitle" placeholder="Case title" />\n\t\t\t\t<textarea class="aicoder-workbench__textarea" rows="7" [(ngModel)]="requestMessage" placeholder="Describe what should change, what evidence to inspect, and what done looks like."></textarea>\n\t\t\t\t<div class="aicoder-workbench__grid">\n\t\t\t\t\t<label>\n\t\t\t\t\t\t<span>Workflow</span>\n\t\t\t\t\t\t<select [(ngModel)]="workflowMode">\n\t\t\t\t\t\t\t<option value="qa_playbook">QA playbook</option>\n\t\t\t\t\t\t\t<option value="implementation">Implementation</option>\n\t\t\t\t\t\t\t<option value="data_change">Data change</option>\n\t\t\t\t\t\t</select>\n\t\t\t\t\t</label>\n\t\t\t\t\t<label>\n\t\t\t\t\t\t<span>Deploy target</span>\n\t\t\t\t\t\t<select [(ngModel)]="deploymentTarget">\n\t\t\t\t\t\t\t<option value="branch_pr">Branch + PR</option>\n\t\t\t\t\t\t\t<option value="test_server">Test server</option>\n\t\t\t\t\t\t\t<option value="live_after_approval">Live after approval</option>\n\t\t\t\t\t\t</select>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="submitRequest()" [disabled]="isSubmitting || !requestMessage.trim()">Queue Case</button>\n\t\t\t</section>\n\n\t\t\t<section class="aicoder-workbench__panel">\n\t\t\t\t<h3>Cases</h3>\n\t\t\t\t@if (!jobs.length && !isLoading) {\n\t\t\t\t\t<p class="aicoder-workbench__muted">No cases yet.</p>\n\t\t\t\t}\n\t\t\t\t<div class="aicoder-workbench__case-list">\n\t\t\t\t\t@for (job of jobs; track job._id) {\n\t\t\t\t\t\t<button type="button" class="aicoder-workbench__case" [class.is-active]="selectedJob?._id === job._id" (click)="selectJob(job)">\n\t\t\t\t\t\t\t<strong>{{ job.title || \'AICoder Case\' }}</strong>\n\t\t\t\t\t\t\t<span>{{ job.status || \'Queued\' }}{{ job.stage ? \' - \' + job.stage : \'\' }}</span>\n\t\t\t\t\t\t\t<small>{{ formatDate(job.updatedAt || job.createdAt) }}</small>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t</section>\n\t\t</div>\n\t}\n\n\t@if (activeTab === \'chat\') {\n\t\t<div class="aicoder-workbench__layout">\n\t\t\t<section class="aicoder-workbench__panel aicoder-workbench__panel--wide">\n\t\t\t\t<h3>{{ selectedJob?.title || \'Select a case\' }}</h3>\n\t\t\t\t<div class="aicoder-workbench__toolbar">\n\t\t\t\t\t<button type="button" (click)="approveSelectedJob()" [disabled]="!selectedJobId || isSubmitting">Approve</button>\n\t\t\t\t\t<button type="button" (click)="createPullRequest()" [disabled]="!selectedJobId || isSubmitting">Create PR</button>\n\t\t\t\t\t<button type="button" (click)="publishSelectedJob()" [disabled]="!selectedJobId || isSubmitting">Publish</button>\n\t\t\t\t\t<button type="button" (click)="openPr()" [disabled]="!resolvePrUrl(selectedJob)">Open PR</button>\n\t\t\t\t</div>\n\t\t\t\t<div class="aicoder-workbench__log">\n\t\t\t\t\t@if (!jobLogs.length) {\n\t\t\t\t\t\t<p class="aicoder-workbench__muted">No log entries loaded.</p>\n\t\t\t\t\t}\n\t\t\t\t\t@for (log of jobLogs; track log._id || log.createdAt || log.message) {\n\t\t\t\t\t\t<div class="aicoder-workbench__log-row">\n\t\t\t\t\t\t\t<span>{{ formatDate(log.createdAt || log.date_created) }}</span>\n\t\t\t\t\t\t\t<p>{{ log.message || log.stage || log.level || \'Log entry\' }}</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t\t<textarea class="aicoder-workbench__textarea" rows="4" [(ngModel)]="chatMessage" placeholder="Send a follow-up to this case."></textarea>\n\t\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="sendChatMessage()" [disabled]="!selectedJobId || !chatMessage.trim() || isSubmitting">Send</button>\n\t\t\t</section>\n\t\t</div>\n\t}\n\n\t@if (activeTab === \'qa\') {\n\t\t<section class="aicoder-workbench__panel">\n\t\t\t<h3>QA Playbook</h3>\n\t\t\t<div class="aicoder-workbench__grid">\n\t\t\t\t<label>\n\t\t\t\t\t<span>Evidence notes</span>\n\t\t\t\t\t<textarea rows="5" [(ngModel)]="evidenceNotes"></textarea>\n\t\t\t\t</label>\n\t\t\t\t<label>\n\t\t\t\t\t<span>QA focus</span>\n\t\t\t\t\t<textarea rows="5" [(ngModel)]="qaFocus"></textarea>\n\t\t\t\t</label>\n\t\t\t\t<label>\n\t\t\t\t\t<span>Risk notes</span>\n\t\t\t\t\t<textarea rows="5" [(ngModel)]="riskNotes"></textarea>\n\t\t\t\t</label>\n\t\t\t\t<label>\n\t\t\t\t\t<span>Data change policy</span>\n\t\t\t\t\t<select [(ngModel)]="dataChangePolicy">\n\t\t\t\t\t\t<option value="review_before_write">Review before write</option>\n\t\t\t\t\t\t<option value="safe_seed_only">Safe seed only</option>\n\t\t\t\t\t\t<option value="no_data_changes">No data changes</option>\n\t\t\t\t\t</select>\n\t\t\t\t</label>\n\t\t\t</div>\n\t\t</section>\n\t}\n\n\t@if (activeTab === \'deploy\') {\n\t\t<section class="aicoder-workbench__panel">\n\t\t\t<h3>Deploy</h3>\n\t\t\t<div class="aicoder-workbench__toolbar">\n\t\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="queueDeploy(\'full\')" [disabled]="isSubmitting">Full Deploy</button>\n\t\t\t\t<button type="button" (click)="queueDeploy(\'client\')" [disabled]="isSubmitting">Client</button>\n\t\t\t\t<button type="button" (click)="queueDeploy(\'backend\')" [disabled]="isSubmitting">Server</button>\n\t\t\t</div>\n\t\t\t<h4>Current</h4>\n\t\t\t@if (!activeDeploys.length) {\n\t\t\t\t<p class="aicoder-workbench__muted">No active deploys.</p>\n\t\t\t}\n\t\t\t@for (op of activeDeploys; track op.id || op.jobId) {\n\t\t\t\t<div class="aicoder-workbench__deploy-row">\n\t\t\t\t\t<strong>{{ op.type || \'Deploy\' }} - {{ op.status }}</strong>\n\t\t\t\t\t<span>{{ op.aws_build_current_phase || op.backend_deploy_step || op.message }}</span>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t<h4>Recent</h4>\n\t\t\t@for (op of recentDeploys; track op.id || op.jobId) {\n\t\t\t\t<div class="aicoder-workbench__deploy-row">\n\t\t\t\t\t<strong>{{ op.type || \'Deploy\' }} - {{ op.status }}</strong>\n\t\t\t\t\t<span>{{ op.message || op.backend_deploy_step || \'Deploy finished\' }}</span>\n\t\t\t\t\t<small>{{ formatDate(op.updatedAt || op.createdAt) }}</small>\n\t\t\t\t</div>\n\t\t\t}\n\t\t</section>\n\t}\n\n\t@if (activeTab === \'git\') {\n\t\t<section class="aicoder-workbench__panel">\n\t\t\t<h3>Git</h3>\n\t\t\t<div class="aicoder-workbench__grid">\n\t\t\t\t<div><span>Repo</span><strong>{{ gitStatus?.repo || \'Not loaded\' }}</strong></div>\n\t\t\t\t<div><span>Main branch</span><strong>{{ gitStatus?.default_branch || \'main\' }}</strong></div>\n\t\t\t\t<div><span>Pushes</span><strong>{{ gitStatus?.push_count ?? \'N/A\' }}</strong></div>\n\t\t\t\t<div><span>Last push</span><strong>{{ formatDate(gitStatus?.last_push) }}</strong></div>\n\t\t\t</div>\n\t\t\t<div class="aicoder-workbench__toolbar">\n\t\t\t\t@if (gitStatus?.repo_url) {\n\t\t\t\t\t<a [href]="gitStatus?.repo_url" target="_blank" rel="noopener">Open Repo</a>\n\t\t\t\t}\n\t\t\t\t<button type="button" (click)="loadGitStatus()">Refresh Git</button>\n\t\t\t</div>\n\t\t</section>\n\t}\n</section>\n',styles:[".aicoder-workbench{display:flex;flex-direction:column;gap:1rem;color:#1f2933}.aicoder-workbench__header{display:flex;align-items:center;justify-content:space-between;gap:1rem}.aicoder-workbench__header p{margin:0 0 .25rem;color:#64748b;font-size:.75rem;font-weight:800;letter-spacing:0;text-transform:uppercase}.aicoder-workbench__header h2{margin:0;font-size:1.35rem;font-weight:800}.aicoder-workbench__tabs,.aicoder-workbench__toolbar{display:flex;flex-wrap:wrap;gap:.5rem}.aicoder-workbench__tabs button,.aicoder-workbench__toolbar button,.aicoder-workbench__toolbar a,.aicoder-workbench__ghost,.aicoder-workbench__primary{border:1px solid #cbd5e1;border-radius:6px;background:#fff;color:#1f2933;font-weight:700;padding:.55rem .8rem;text-decoration:none}.aicoder-workbench__tabs button.is-active,.aicoder-workbench__primary{border-color:#2f7d73;background:#2f7d73;color:#fff}.aicoder-workbench__tabs button:disabled,.aicoder-workbench__toolbar button:disabled,.aicoder-workbench__primary:disabled{cursor:not-allowed;opacity:.55}.aicoder-workbench__layout{display:grid;grid-template-columns:minmax(0,1fr) minmax(280px,.7fr);gap:1rem}.aicoder-workbench__panel{border:1px solid #dbe3ec;border-radius:8px;background:#fff;padding:1rem}.aicoder-workbench__panel--wide{grid-column:1/-1}.aicoder-workbench__panel h3,.aicoder-workbench__panel h4{margin:0 0 .75rem;font-size:1rem;font-weight:800}.aicoder-workbench__panel h4{margin-top:1rem;color:#64748b;font-size:.8rem;text-transform:uppercase}.aicoder-workbench__input,.aicoder-workbench__textarea,.aicoder-workbench select,.aicoder-workbench textarea{width:100%;border:1px solid #cbd5e1;border-radius:6px;background:#fff;color:#1f2933;padding:.65rem .75rem}.aicoder-workbench__textarea,.aicoder-workbench textarea{resize:vertical}.aicoder-workbench__input,.aicoder-workbench__textarea{margin-bottom:.75rem}.aicoder-workbench__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:.75rem;margin-bottom:.75rem}.aicoder-workbench__grid span,.aicoder-workbench__grid label span{display:block;margin-bottom:.3rem;color:#64748b;font-size:.75rem;font-weight:800;text-transform:uppercase}.aicoder-workbench__case-list,.aicoder-workbench__log{display:flex;flex-direction:column;gap:.5rem;max-height:460px;overflow:auto}.aicoder-workbench__case,.aicoder-workbench__deploy-row,.aicoder-workbench__log-row{display:flex;flex-direction:column;align-items:flex-start;gap:.2rem;width:100%;border:1px solid #e2e8f0;border-radius:7px;background:#f8fafc;padding:.75rem;text-align:left}.aicoder-workbench__case.is-active{border-color:#2f7d73;background:#ecfdf5}.aicoder-workbench__case span,.aicoder-workbench__case small,.aicoder-workbench__deploy-row span,.aicoder-workbench__deploy-row small,.aicoder-workbench__log-row span{color:#64748b;font-size:.82rem}.aicoder-workbench__log{min-height:220px;margin-bottom:.75rem}.aicoder-workbench__log-row p{margin:0}.aicoder-workbench__notice{border-radius:6px;padding:.75rem .9rem;font-weight:700}.aicoder-workbench__notice--error{border:1px solid #fecaca;background:#fff1f2;color:#991b1b}.aicoder-workbench__notice--success{border:1px solid #bbf7d0;background:#f0fdf4;color:#166534}.aicoder-workbench__muted{margin:0;color:#64748b}.aicoder-workbench--compact .aicoder-workbench__layout{grid-template-columns:1fr}@media(max-width:900px){.aicoder-workbench__layout{grid-template-columns:1fr}}\n"],dependencies:[{kind:"directive",type:i2$1.NgSelectOption,selector:"option",inputs:["ngValue","value"]},{kind:"directive",type:i2$1.ɵNgSelectMultipleOption,selector:"option",inputs:["ngValue","value"]},{kind:"directive",type:i2$1.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:i2$1.SelectControlValueAccessor,selector:"select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]",inputs:["compareWith"]},{kind:"directive",type:i2$1.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:i2$1.NgModel,selector:"[ngModel]:not([formControlName]):not([formControl])",inputs:["name","disabled","ngModel","ngModelOptions"],outputs:["ngModelChange"],exportAs:["ngModel"]}]})}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderWorkbenchComponent,decorators:[{type:Component,args:[{selector:"resolveio-client-lib-aicoder-workbench",standalone:!1,template:'<section class="aicoder-workbench" [class.aicoder-workbench--compact]="compact">\n\t@if (showHeader) {\n\t\t<header class="aicoder-workbench__header">\n\t\t\t<div>\n\t\t\t\t<p>AICoder</p>\n\t\t\t\t<h2>{{ appName || \'AICoder App\' }}</h2>\n\t\t\t</div>\n\t\t\t<button type="button" class="aicoder-workbench__ghost" (click)="refresh()" [disabled]="isLoading || isSubmitting">Refresh</button>\n\t\t</header>\n\t}\n\n\t<nav class="aicoder-workbench__tabs" aria-label="AICoder workbench tabs">\n\t\t@for (tab of tabs; track tab.id) {\n\t\t\t<button type="button" [class.is-active]="activeTab === tab.id" (click)="setTab(tab.id)">\n\t\t\t\t{{ tab.label }}\n\t\t\t</button>\n\t\t}\n\t</nav>\n\n\t@if (errorMessage) {\n\t\t<div class="aicoder-workbench__notice aicoder-workbench__notice--error">{{ errorMessage }}</div>\n\t}\n\t@if (successMessage) {\n\t\t<div class="aicoder-workbench__notice aicoder-workbench__notice--success">{{ successMessage }}</div>\n\t}\n\n\t@if (activeTab === \'cases\') {\n\t\t<div class="aicoder-workbench__layout">\n\t\t\t<section class="aicoder-workbench__panel">\n\t\t\t\t<h3>New Case</h3>\n\t\t\t\t<input class="aicoder-workbench__input" [(ngModel)]="requestTitle" placeholder="Case title" />\n\t\t\t\t<textarea class="aicoder-workbench__textarea" rows="7" [(ngModel)]="requestMessage" placeholder="Describe what should change, what evidence to inspect, and what done looks like."></textarea>\n\t\t\t\t<div class="aicoder-workbench__grid">\n\t\t\t\t\t<label>\n\t\t\t\t\t\t<span>Workflow</span>\n\t\t\t\t\t\t<select [(ngModel)]="workflowMode">\n\t\t\t\t\t\t\t<option value="qa_playbook">QA playbook</option>\n\t\t\t\t\t\t\t<option value="implementation">Implementation</option>\n\t\t\t\t\t\t\t<option value="data_change">Data change</option>\n\t\t\t\t\t\t</select>\n\t\t\t\t\t</label>\n\t\t\t\t\t<label>\n\t\t\t\t\t\t<span>Deploy target</span>\n\t\t\t\t\t\t<select [(ngModel)]="deploymentTarget">\n\t\t\t\t\t\t\t<option value="branch_pr">Branch + PR</option>\n\t\t\t\t\t\t\t<option value="test_server">Test server</option>\n\t\t\t\t\t\t\t<option value="live_after_approval">Live after approval</option>\n\t\t\t\t\t\t</select>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="submitRequest()" [disabled]="isSubmitting || !requestMessage.trim()">Queue Case</button>\n\t\t\t</section>\n\n\t\t\t<section class="aicoder-workbench__panel">\n\t\t\t\t<h3>Cases</h3>\n\t\t\t\t@if (!jobs.length && !isLoading) {\n\t\t\t\t\t<p class="aicoder-workbench__muted">No cases yet.</p>\n\t\t\t\t}\n\t\t\t\t<div class="aicoder-workbench__case-list">\n\t\t\t\t\t@for (job of jobs; track job._id) {\n\t\t\t\t\t\t<button type="button" class="aicoder-workbench__case" [class.is-active]="selectedJob?._id === job._id" (click)="selectJob(job)">\n\t\t\t\t\t\t\t<strong>{{ job.title || \'AICoder Case\' }}</strong>\n\t\t\t\t\t\t\t<span>{{ job.status || \'Queued\' }}{{ job.stage ? \' - \' + job.stage : \'\' }}</span>\n\t\t\t\t\t\t\t<small>{{ formatDate(job.updatedAt || job.createdAt) }}</small>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t</section>\n\t\t</div>\n\t}\n\n\t@if (activeTab === \'chat\') {\n\t\t<div class="aicoder-workbench__layout">\n\t\t\t<section class="aicoder-workbench__panel aicoder-workbench__panel--wide">\n\t\t\t\t<h3>{{ selectedJob?.title || \'Select a case\' }}</h3>\n\t\t\t\t<div class="aicoder-workbench__toolbar">\n\t\t\t\t\t<button type="button" (click)="approveSelectedJob()" [disabled]="!selectedJobId || isSubmitting">Approve</button>\n\t\t\t\t\t<button type="button" (click)="createPullRequest()" [disabled]="!selectedJobId || isSubmitting">Create PR</button>\n\t\t\t\t\t<button type="button" (click)="publishSelectedJob()" [disabled]="!selectedJobId || isSubmitting">Publish</button>\n\t\t\t\t\t<button type="button" (click)="openPr()" [disabled]="!resolvePrUrl(selectedJob)">Open PR</button>\n\t\t\t\t</div>\n\t\t\t\t<div class="aicoder-workbench__log">\n\t\t\t\t\t@if (!jobLogs.length) {\n\t\t\t\t\t\t<p class="aicoder-workbench__muted">No log entries loaded.</p>\n\t\t\t\t\t}\n\t\t\t\t\t@for (log of jobLogs; track log._id || log.createdAt || log.message) {\n\t\t\t\t\t\t<div class="aicoder-workbench__log-row">\n\t\t\t\t\t\t\t<span>{{ formatDate(log.createdAt || log.date_created) }}</span>\n\t\t\t\t\t\t\t<p>{{ log.message || log.stage || log.level || \'Log entry\' }}</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t\t<textarea class="aicoder-workbench__textarea" rows="4" [(ngModel)]="chatMessage" placeholder="Send a follow-up to this case."></textarea>\n\t\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="sendChatMessage()" [disabled]="!selectedJobId || !chatMessage.trim() || isSubmitting">Send</button>\n\t\t\t</section>\n\t\t</div>\n\t}\n\n\t@if (activeTab === \'qa\') {\n\t\t<section class="aicoder-workbench__panel">\n\t\t\t<h3>QA Playbook</h3>\n\t\t\t<div class="aicoder-workbench__grid">\n\t\t\t\t<label>\n\t\t\t\t\t<span>Evidence notes</span>\n\t\t\t\t\t<textarea rows="5" [(ngModel)]="evidenceNotes"></textarea>\n\t\t\t\t</label>\n\t\t\t\t<label>\n\t\t\t\t\t<span>QA focus</span>\n\t\t\t\t\t<textarea rows="5" [(ngModel)]="qaFocus"></textarea>\n\t\t\t\t</label>\n\t\t\t\t<label>\n\t\t\t\t\t<span>Risk notes</span>\n\t\t\t\t\t<textarea rows="5" [(ngModel)]="riskNotes"></textarea>\n\t\t\t\t</label>\n\t\t\t\t<label>\n\t\t\t\t\t<span>Data change policy</span>\n\t\t\t\t\t<select [(ngModel)]="dataChangePolicy">\n\t\t\t\t\t\t<option value="review_before_write">Review before write</option>\n\t\t\t\t\t\t<option value="safe_seed_only">Safe seed only</option>\n\t\t\t\t\t\t<option value="no_data_changes">No data changes</option>\n\t\t\t\t\t</select>\n\t\t\t\t</label>\n\t\t\t</div>\n\t\t</section>\n\t}\n\n\t@if (activeTab === \'deploy\') {\n\t\t<section class="aicoder-workbench__panel">\n\t\t\t<h3>Deploy</h3>\n\t\t\t<div class="aicoder-workbench__toolbar">\n\t\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="queueDeploy(\'full\')" [disabled]="isSubmitting">Full Deploy</button>\n\t\t\t\t<button type="button" (click)="queueDeploy(\'client\')" [disabled]="isSubmitting">Client</button>\n\t\t\t\t<button type="button" (click)="queueDeploy(\'backend\')" [disabled]="isSubmitting">Server</button>\n\t\t\t</div>\n\t\t\t<h4>Current</h4>\n\t\t\t@if (!activeDeploys.length) {\n\t\t\t\t<p class="aicoder-workbench__muted">No active deploys.</p>\n\t\t\t}\n\t\t\t@for (op of activeDeploys; track op.id || op.jobId) {\n\t\t\t\t<div class="aicoder-workbench__deploy-row">\n\t\t\t\t\t<strong>{{ op.type || \'Deploy\' }} - {{ op.status }}</strong>\n\t\t\t\t\t<span>{{ op.aws_build_current_phase || op.backend_deploy_step || op.message }}</span>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t<h4>Recent</h4>\n\t\t\t@for (op of recentDeploys; track op.id || op.jobId) {\n\t\t\t\t<div class="aicoder-workbench__deploy-row">\n\t\t\t\t\t<strong>{{ op.type || \'Deploy\' }} - {{ op.status }}</strong>\n\t\t\t\t\t<span>{{ op.message || op.backend_deploy_step || \'Deploy finished\' }}</span>\n\t\t\t\t\t<small>{{ formatDate(op.updatedAt || op.createdAt) }}</small>\n\t\t\t\t</div>\n\t\t\t}\n\t\t</section>\n\t}\n\n\t@if (activeTab === \'git\') {\n\t\t<section class="aicoder-workbench__panel">\n\t\t\t<h3>Git</h3>\n\t\t\t<div class="aicoder-workbench__grid">\n\t\t\t\t<div><span>Repo</span><strong>{{ gitStatus?.repo || \'Not loaded\' }}</strong></div>\n\t\t\t\t<div><span>Main branch</span><strong>{{ gitStatus?.default_branch || \'main\' }}</strong></div>\n\t\t\t\t<div><span>Pushes</span><strong>{{ gitStatus?.push_count ?? \'N/A\' }}</strong></div>\n\t\t\t\t<div><span>Last push</span><strong>{{ formatDate(gitStatus?.last_push) }}</strong></div>\n\t\t\t</div>\n\t\t\t<div class="aicoder-workbench__toolbar">\n\t\t\t\t@if (gitStatus?.repo_url) {\n\t\t\t\t\t<a [href]="gitStatus?.repo_url" target="_blank" rel="noopener">Open Repo</a>\n\t\t\t\t}\n\t\t\t\t<button type="button" (click)="loadGitStatus()">Refresh Git</button>\n\t\t\t</div>\n\t\t</section>\n\t}\n</section>\n',styles:[".aicoder-workbench{display:flex;flex-direction:column;gap:1rem;color:#1f2933}.aicoder-workbench__header{display:flex;align-items:center;justify-content:space-between;gap:1rem}.aicoder-workbench__header p{margin:0 0 .25rem;color:#64748b;font-size:.75rem;font-weight:800;letter-spacing:0;text-transform:uppercase}.aicoder-workbench__header h2{margin:0;font-size:1.35rem;font-weight:800}.aicoder-workbench__tabs,.aicoder-workbench__toolbar{display:flex;flex-wrap:wrap;gap:.5rem}.aicoder-workbench__tabs button,.aicoder-workbench__toolbar button,.aicoder-workbench__toolbar a,.aicoder-workbench__ghost,.aicoder-workbench__primary{border:1px solid #cbd5e1;border-radius:6px;background:#fff;color:#1f2933;font-weight:700;padding:.55rem .8rem;text-decoration:none}.aicoder-workbench__tabs button.is-active,.aicoder-workbench__primary{border-color:#2f7d73;background:#2f7d73;color:#fff}.aicoder-workbench__tabs button:disabled,.aicoder-workbench__toolbar button:disabled,.aicoder-workbench__primary:disabled{cursor:not-allowed;opacity:.55}.aicoder-workbench__layout{display:grid;grid-template-columns:minmax(0,1fr) minmax(280px,.7fr);gap:1rem}.aicoder-workbench__panel{border:1px solid #dbe3ec;border-radius:8px;background:#fff;padding:1rem}.aicoder-workbench__panel--wide{grid-column:1/-1}.aicoder-workbench__panel h3,.aicoder-workbench__panel h4{margin:0 0 .75rem;font-size:1rem;font-weight:800}.aicoder-workbench__panel h4{margin-top:1rem;color:#64748b;font-size:.8rem;text-transform:uppercase}.aicoder-workbench__input,.aicoder-workbench__textarea,.aicoder-workbench select,.aicoder-workbench textarea{width:100%;border:1px solid #cbd5e1;border-radius:6px;background:#fff;color:#1f2933;padding:.65rem .75rem}.aicoder-workbench__textarea,.aicoder-workbench textarea{resize:vertical}.aicoder-workbench__input,.aicoder-workbench__textarea{margin-bottom:.75rem}.aicoder-workbench__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:.75rem;margin-bottom:.75rem}.aicoder-workbench__grid span,.aicoder-workbench__grid label span{display:block;margin-bottom:.3rem;color:#64748b;font-size:.75rem;font-weight:800;text-transform:uppercase}.aicoder-workbench__case-list,.aicoder-workbench__log{display:flex;flex-direction:column;gap:.5rem;max-height:460px;overflow:auto}.aicoder-workbench__case,.aicoder-workbench__deploy-row,.aicoder-workbench__log-row{display:flex;flex-direction:column;align-items:flex-start;gap:.2rem;width:100%;border:1px solid #e2e8f0;border-radius:7px;background:#f8fafc;padding:.75rem;text-align:left}.aicoder-workbench__case.is-active{border-color:#2f7d73;background:#ecfdf5}.aicoder-workbench__case span,.aicoder-workbench__case small,.aicoder-workbench__deploy-row span,.aicoder-workbench__deploy-row small,.aicoder-workbench__log-row span{color:#64748b;font-size:.82rem}.aicoder-workbench__log{min-height:220px;margin-bottom:.75rem}.aicoder-workbench__log-row p{margin:0}.aicoder-workbench__notice{border-radius:6px;padding:.75rem .9rem;font-weight:700}.aicoder-workbench__notice--error{border:1px solid #fecaca;background:#fff1f2;color:#991b1b}.aicoder-workbench__notice--success{border:1px solid #bbf7d0;background:#f0fdf4;color:#166534}.aicoder-workbench__muted{margin:0;color:#64748b}.aicoder-workbench--compact .aicoder-workbench__layout{grid-template-columns:1fr}@media(max-width:900px){.aicoder-workbench__layout{grid-template-columns:1fr}}\n"]}]}],ctorParameters:()=>[{type:AICoderWorkbenchApiService}],propDecorators:{appId:[{type:Input}],appName:[{type:Input}],apiBase:[{type:Input}],defaultTab:[{type:Input}],compact:[{type:Input}],showHeader:[{type:Input}],autoRefresh:[{type:Input}]}});class AICoderDashboardModule{static"ɵfac"=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardModule,deps:[],target:i0.ɵɵFactoryTarget.NgModule});static"ɵmod"=i0.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardModule,declarations:[AICoderDashboardComponent,AICoderWorkbenchComponent],imports:[CommonModule,FormsModule,HttpClientModule],exports:[AICoderDashboardComponent,AICoderWorkbenchComponent]});static"ɵinj"=i0.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardModule,imports:[CommonModule,FormsModule,HttpClientModule]})}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardModule,decorators:[{type:NgModule,args:[{imports:[CommonModule,FormsModule,HttpClientModule],declarations:[AICoderDashboardComponent,AICoderWorkbenchComponent],exports:[AICoderDashboardComponent,AICoderWorkbenchComponent]}]}]});export{AICoderDashboardComponent,AICoderDashboardModule,AICoderDashboardService,AICoderWorkbenchApiService,AICoderWorkbenchComponent};
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@resolveio/client-lib-aicoder-dashboard",
|
|
3
|
-
"version": "21.0.
|
|
3
|
+
"version": "21.0.3",
|
|
4
4
|
"peerDependencies": {
|
|
5
|
-
"@angular/common": "21.
|
|
6
|
-
"@angular/core": "21.
|
|
5
|
+
"@angular/common": "^21.0.0",
|
|
6
|
+
"@angular/core": "^21.0.0",
|
|
7
|
+
"@resolveio/client-lib-core": "^21.0.0"
|
|
7
8
|
},
|
|
8
9
|
"dependencies": {
|
|
9
10
|
"tslib": "^2.8.1"
|
|
Binary file
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { EventEmitter } from '@angular/core';
|
|
3
|
-
import * as
|
|
2
|
+
import { EventEmitter, OnInit, OnChanges, OnDestroy, SimpleChanges } from '@angular/core';
|
|
3
|
+
import * as i5 from '@angular/common/http';
|
|
4
|
+
import { HttpClient } from '@angular/common/http';
|
|
5
|
+
import { TokenManagerService } from '@resolveio/client-lib-core';
|
|
6
|
+
import { Observable } from 'rxjs';
|
|
7
|
+
import * as i3 from '@angular/common';
|
|
8
|
+
import * as i4 from '@angular/forms';
|
|
4
9
|
|
|
5
|
-
type AICoderDashboardTab = 'overview' | 'database' | 'infrastructure' | 'code_changes';
|
|
10
|
+
type AICoderDashboardTab = 'overview' | 'database' | 'infrastructure' | 'code_changes' | 'subscription';
|
|
6
11
|
type AICoderTier = 'small' | 'medium' | 'large';
|
|
12
|
+
type AICoderWorkbenchTab = 'cases' | 'chat' | 'qa' | 'deploy' | 'git';
|
|
7
13
|
interface AICoderTierOption {
|
|
8
14
|
id: AICoderTier;
|
|
9
15
|
label: string;
|
|
@@ -32,6 +38,72 @@ interface AICoderDashboardSummary {
|
|
|
32
38
|
last_deployed_at: string;
|
|
33
39
|
recent_updates: string[];
|
|
34
40
|
};
|
|
41
|
+
subscription?: {
|
|
42
|
+
current_package: string;
|
|
43
|
+
current_tier: string;
|
|
44
|
+
package_price_label: string;
|
|
45
|
+
billing_status: string;
|
|
46
|
+
billing_mode: string;
|
|
47
|
+
subscription_status: string;
|
|
48
|
+
current_period_end: string;
|
|
49
|
+
manage_url: string;
|
|
50
|
+
can_downgrade: boolean;
|
|
51
|
+
downgrade_reason: string;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
interface AICoderWorkbenchJob {
|
|
55
|
+
_id: string;
|
|
56
|
+
title?: string;
|
|
57
|
+
status?: string;
|
|
58
|
+
stage?: string;
|
|
59
|
+
phase?: string;
|
|
60
|
+
project?: string;
|
|
61
|
+
createdAt?: string;
|
|
62
|
+
updatedAt?: string;
|
|
63
|
+
pr_url?: string;
|
|
64
|
+
pull_request_url?: string;
|
|
65
|
+
branch?: string;
|
|
66
|
+
github_branch?: string;
|
|
67
|
+
summary?: string;
|
|
68
|
+
last_message?: string;
|
|
69
|
+
}
|
|
70
|
+
interface AICoderWorkbenchLog {
|
|
71
|
+
_id?: string;
|
|
72
|
+
message?: string;
|
|
73
|
+
level?: string;
|
|
74
|
+
stage?: string;
|
|
75
|
+
createdAt?: string;
|
|
76
|
+
date_created?: string;
|
|
77
|
+
}
|
|
78
|
+
interface AICoderWorkbenchDeployOperation {
|
|
79
|
+
id?: string;
|
|
80
|
+
type?: string;
|
|
81
|
+
status?: string;
|
|
82
|
+
message?: string;
|
|
83
|
+
jobId?: string;
|
|
84
|
+
createdAt?: string;
|
|
85
|
+
updatedAt?: string;
|
|
86
|
+
aws_build_current_phase?: string;
|
|
87
|
+
backend_deploy_status?: string;
|
|
88
|
+
backend_deploy_step?: string;
|
|
89
|
+
}
|
|
90
|
+
interface AICoderWorkbenchDeployJob {
|
|
91
|
+
id?: string;
|
|
92
|
+
type?: string;
|
|
93
|
+
status?: string;
|
|
94
|
+
error?: string;
|
|
95
|
+
date_start?: string;
|
|
96
|
+
date_end?: string;
|
|
97
|
+
current_step?: string;
|
|
98
|
+
}
|
|
99
|
+
interface AICoderWorkbenchGitStatus {
|
|
100
|
+
repo?: string;
|
|
101
|
+
repo_url?: string;
|
|
102
|
+
default_branch?: string;
|
|
103
|
+
push_count?: number;
|
|
104
|
+
last_push?: string;
|
|
105
|
+
local_path?: string;
|
|
106
|
+
local_path_valid?: boolean;
|
|
35
107
|
}
|
|
36
108
|
|
|
37
109
|
declare class AICoderDashboardService {
|
|
@@ -51,20 +123,116 @@ declare class AICoderDashboardComponent {
|
|
|
51
123
|
tabChange: EventEmitter<AICoderDashboardTab>;
|
|
52
124
|
retry: EventEmitter<void>;
|
|
53
125
|
tierChange: EventEmitter<AICoderTier>;
|
|
126
|
+
manageSubscription: EventEmitter<string>;
|
|
54
127
|
constructor(dashboardService: AICoderDashboardService);
|
|
55
128
|
setTab(tab: AICoderDashboardTab): void;
|
|
56
129
|
requestRetry(): void;
|
|
57
130
|
requestTierChange(tier: AICoderTier): void;
|
|
131
|
+
requestManageSubscription(): void;
|
|
58
132
|
isCurrentTier(tier: AICoderTier): boolean;
|
|
133
|
+
hasManageSubscriptionUrl(): boolean;
|
|
134
|
+
private resolveManageSubscriptionUrl;
|
|
59
135
|
static ɵfac: i0.ɵɵFactoryDeclaration<AICoderDashboardComponent, never>;
|
|
60
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<AICoderDashboardComponent, "resolveio-client-lib-aicoder-dashboard", never, { "summary": { "alias": "summary"; "required": false; }; "loading": { "alias": "loading"; "required": false; }; "errorMessage": { "alias": "errorMessage"; "required": false; }; "activeTab": { "alias": "activeTab"; "required": false; }; "tierOptions": { "alias": "tierOptions"; "required": false; }; }, { "tabChange": "tabChange"; "retry": "retry"; "tierChange": "tierChange"; }, never, never, false, never>;
|
|
136
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<AICoderDashboardComponent, "resolveio-client-lib-aicoder-dashboard", never, { "summary": { "alias": "summary"; "required": false; }; "loading": { "alias": "loading"; "required": false; }; "errorMessage": { "alias": "errorMessage"; "required": false; }; "activeTab": { "alias": "activeTab"; "required": false; }; "tierOptions": { "alias": "tierOptions"; "required": false; }; }, { "tabChange": "tabChange"; "retry": "retry"; "tierChange": "tierChange"; "manageSubscription": "manageSubscription"; }, never, never, false, never>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
declare class AICoderWorkbenchApiService {
|
|
140
|
+
private readonly http;
|
|
141
|
+
private readonly tokenManager;
|
|
142
|
+
private readonly sessionHeaderName;
|
|
143
|
+
private readonly sessionRefreshSkewMs;
|
|
144
|
+
private readonly defaultSessionTtlMs;
|
|
145
|
+
private sessionByApiBase;
|
|
146
|
+
private exchangeByApiBase;
|
|
147
|
+
constructor(http: HttpClient, tokenManager: TokenManagerService);
|
|
148
|
+
get<T>(apiBase: string, path: string): Observable<T>;
|
|
149
|
+
post<T>(apiBase: string, path: string, body?: any): Observable<T>;
|
|
150
|
+
patch<T>(apiBase: string, path: string, body?: any): Observable<T>;
|
|
151
|
+
resolveDefaultApiBase(): string;
|
|
152
|
+
private authHeaders;
|
|
153
|
+
private ensureSessionToken;
|
|
154
|
+
private resolveSessionExpiresAtMs;
|
|
155
|
+
private buildUrl;
|
|
156
|
+
private normalizeApiBase;
|
|
157
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<AICoderWorkbenchApiService, never>;
|
|
158
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<AICoderWorkbenchApiService>;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
declare class AICoderWorkbenchComponent implements OnInit, OnChanges, OnDestroy {
|
|
162
|
+
private readonly api;
|
|
163
|
+
appId: string;
|
|
164
|
+
appName: string;
|
|
165
|
+
apiBase: string;
|
|
166
|
+
defaultTab: AICoderWorkbenchTab;
|
|
167
|
+
compact: boolean;
|
|
168
|
+
showHeader: boolean;
|
|
169
|
+
autoRefresh: boolean;
|
|
170
|
+
activeTab: AICoderWorkbenchTab;
|
|
171
|
+
jobs: AICoderWorkbenchJob[];
|
|
172
|
+
selectedJob: AICoderWorkbenchJob | null;
|
|
173
|
+
jobLogs: AICoderWorkbenchLog[];
|
|
174
|
+
deployOperations: AICoderWorkbenchDeployOperation[];
|
|
175
|
+
deployJobs: AICoderWorkbenchDeployJob[];
|
|
176
|
+
gitStatus: AICoderWorkbenchGitStatus | null;
|
|
177
|
+
isLoading: boolean;
|
|
178
|
+
isSubmitting: boolean;
|
|
179
|
+
errorMessage: string;
|
|
180
|
+
successMessage: string;
|
|
181
|
+
requestTitle: string;
|
|
182
|
+
requestMessage: string;
|
|
183
|
+
chatMessage: string;
|
|
184
|
+
workflowMode: string;
|
|
185
|
+
requestKind: string;
|
|
186
|
+
dataChangePolicy: string;
|
|
187
|
+
deploymentTarget: string;
|
|
188
|
+
evidenceNotes: string;
|
|
189
|
+
qaFocus: string;
|
|
190
|
+
riskNotes: string;
|
|
191
|
+
private refreshSub?;
|
|
192
|
+
readonly tabs: Array<{
|
|
193
|
+
id: AICoderWorkbenchTab;
|
|
194
|
+
label: string;
|
|
195
|
+
}>;
|
|
196
|
+
constructor(api: AICoderWorkbenchApiService);
|
|
197
|
+
ngOnInit(): void;
|
|
198
|
+
ngOnChanges(changes: SimpleChanges): void;
|
|
199
|
+
ngOnDestroy(): void;
|
|
200
|
+
setTab(tab: AICoderWorkbenchTab): void;
|
|
201
|
+
refresh(): void;
|
|
202
|
+
selectJob(job: AICoderWorkbenchJob): void;
|
|
203
|
+
submitRequest(): void;
|
|
204
|
+
sendChatMessage(): void;
|
|
205
|
+
approveSelectedJob(): void;
|
|
206
|
+
createPullRequest(): void;
|
|
207
|
+
publishSelectedJob(): void;
|
|
208
|
+
queueDeploy(target: 'full' | 'client' | 'backend'): void;
|
|
209
|
+
openPr(job?: AICoderWorkbenchJob | null): void;
|
|
210
|
+
loadJobs(): void;
|
|
211
|
+
loadJobLogs(jobId: string): void;
|
|
212
|
+
loadDeployHistory(): void;
|
|
213
|
+
loadGitStatus(): void;
|
|
214
|
+
get normalizedAppId(): string;
|
|
215
|
+
get selectedJobId(): string;
|
|
216
|
+
get activeDeploys(): AICoderWorkbenchDeployOperation[];
|
|
217
|
+
get recentDeploys(): AICoderWorkbenchDeployOperation[];
|
|
218
|
+
isActiveJob(job: AICoderWorkbenchJob): boolean;
|
|
219
|
+
resolvePrUrl(job: AICoderWorkbenchJob | null): string;
|
|
220
|
+
formatDate(value: any): string;
|
|
221
|
+
private runJobAction;
|
|
222
|
+
private mergeJob;
|
|
223
|
+
private normalizeJob;
|
|
224
|
+
private startAutoRefresh;
|
|
225
|
+
private clearMessages;
|
|
226
|
+
private handleError;
|
|
227
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<AICoderWorkbenchComponent, never>;
|
|
228
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<AICoderWorkbenchComponent, "resolveio-client-lib-aicoder-workbench", never, { "appId": { "alias": "appId"; "required": false; }; "appName": { "alias": "appName"; "required": false; }; "apiBase": { "alias": "apiBase"; "required": false; }; "defaultTab": { "alias": "defaultTab"; "required": false; }; "compact": { "alias": "compact"; "required": false; }; "showHeader": { "alias": "showHeader"; "required": false; }; "autoRefresh": { "alias": "autoRefresh"; "required": false; }; }, {}, never, never, false, never>;
|
|
61
229
|
}
|
|
62
230
|
|
|
63
231
|
declare class AICoderDashboardModule {
|
|
64
232
|
static ɵfac: i0.ɵɵFactoryDeclaration<AICoderDashboardModule, never>;
|
|
65
|
-
static ɵmod: i0.ɵɵNgModuleDeclaration<AICoderDashboardModule, [typeof AICoderDashboardComponent], [typeof
|
|
233
|
+
static ɵmod: i0.ɵɵNgModuleDeclaration<AICoderDashboardModule, [typeof AICoderDashboardComponent, typeof AICoderWorkbenchComponent], [typeof i3.CommonModule, typeof i4.FormsModule, typeof i5.HttpClientModule], [typeof AICoderDashboardComponent, typeof AICoderWorkbenchComponent]>;
|
|
66
234
|
static ɵinj: i0.ɵɵInjectorDeclaration<AICoderDashboardModule>;
|
|
67
235
|
}
|
|
68
236
|
|
|
69
|
-
export { AICoderDashboardComponent, AICoderDashboardModule, AICoderDashboardService };
|
|
70
|
-
export type { AICoderDashboardSummary, AICoderDashboardTab, AICoderTier, AICoderTierOption };
|
|
237
|
+
export { AICoderDashboardComponent, AICoderDashboardModule, AICoderDashboardService, AICoderWorkbenchApiService, AICoderWorkbenchComponent };
|
|
238
|
+
export type { AICoderDashboardSummary, AICoderDashboardTab, AICoderTier, AICoderTierOption, AICoderWorkbenchDeployJob, AICoderWorkbenchDeployOperation, AICoderWorkbenchGitStatus, AICoderWorkbenchJob, AICoderWorkbenchLog, AICoderWorkbenchTab };
|