add tenant subject list
This commit is contained in:
parent
b1519f4307
commit
8a0dbf71dc
@ -0,0 +1,6 @@
|
||||
export interface SubjectListDto {
|
||||
id: string;
|
||||
name: string;
|
||||
isOwner: boolean;
|
||||
visibility: string;
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
import {Injectable} from "@angular/core";
|
||||
import {lastValueFrom, Observable} from "rxjs";
|
||||
import {PalantirService} from "./palantir.service";
|
||||
import {PalantirService} from '../palantir.service';
|
||||
import {SubjectListDto} from './dtos/subject-list-dto';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -16,4 +17,12 @@ export class SubjectService extends PalantirService {
|
||||
meAsync(onError?: (error: any) => void): Promise<{ "name": string }> {
|
||||
return lastValueFrom(this.me$(onError));
|
||||
}
|
||||
|
||||
forTenant$(tenantId: string, onError?: (error: any) => void): Observable<SubjectListDto[]> {
|
||||
return this.handleRequest(this.http.get<SubjectListDto[]>(this.baseUrl + '/for-tenant/' + tenantId), onError);
|
||||
}
|
||||
|
||||
forTenantAsync(tenantId: string, onError?: (error: any) => void): Promise<SubjectListDto[]> {
|
||||
return lastValueFrom(this.forTenant$(tenantId, onError));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
@if (loading()) {
|
||||
Loading...
|
||||
} @else {
|
||||
@for (subject of subjects(); track subject.id) {
|
||||
<app-panel class="neutral-80">
|
||||
<span class="title" [ngClass]="{'owner': subject.isOwner}">{{subject.name}}</span>
|
||||
<span class="actions">
|
||||
<button [routerLink]="'subject/' + subject.id" class="primary outline">View Details</button>
|
||||
</span>
|
||||
</app-panel>
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: .5rem;
|
||||
|
||||
app-panel {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&.owner {
|
||||
&::before {
|
||||
background-color: var(--primary-30);
|
||||
color: var(--neutral-90);
|
||||
font-size: 0.7rem;
|
||||
line-height: 0.7rem;
|
||||
padding: 0.15rem 0.3rem;
|
||||
font-weight: bold;
|
||||
border-radius: 0.5rem;
|
||||
margin-right: 1ch;
|
||||
content: 'Owner';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SubjectListComponent } from './subject-list.component';
|
||||
|
||||
describe('SubjectListComponent', () => {
|
||||
let component: SubjectListComponent;
|
||||
let fixture: ComponentFixture<SubjectListComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [SubjectListComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SubjectListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,38 @@
|
||||
import {Component, inject, input, OnInit, signal} from '@angular/core';
|
||||
import {PanelComponent} from '../../panel/panel.component';
|
||||
import {RouterLink} from '@angular/router';
|
||||
import {NgClass} from '@angular/common';
|
||||
import {SubjectService} from '../../../clients/gandalf/mithrandir/subject/subject.service';
|
||||
import {SubjectListDto} from '../../../clients/gandalf/mithrandir/subject/dtos/subject-list-dto';
|
||||
|
||||
@Component({
|
||||
selector: 'app-subject-list',
|
||||
imports: [
|
||||
PanelComponent,
|
||||
RouterLink,
|
||||
NgClass
|
||||
],
|
||||
templateUrl: './subject-list.component.html',
|
||||
styleUrl: './subject-list.component.scss',
|
||||
})
|
||||
export class SubjectListComponent implements OnInit {
|
||||
|
||||
public tenantId = input<string>()
|
||||
|
||||
protected loading = signal<boolean>(true);
|
||||
protected subjects = signal<SubjectListDto[]>([]);
|
||||
|
||||
private subjectService = inject(SubjectService);
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
|
||||
const tenantId = this.tenantId();
|
||||
|
||||
if (tenantId) {
|
||||
const subjects = await this.subjectService.forTenantAsync(tenantId);
|
||||
this.subjects.set(subjects);
|
||||
this.loading.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
<app-tab-group [tabs]="tabs" (tabChanged)="tabChanged($event)" [activeTabId]="activeTabId()">
|
||||
|
||||
<ng-template tabId="subjects" let-tab>
|
||||
{{tab.name}} - content
|
||||
<app-subject-list [tenantId]="tenantId()!"></app-subject-list>
|
||||
</ng-template>
|
||||
|
||||
<ng-template tabId="apps" let-tab>
|
||||
|
||||
@ -2,12 +2,14 @@ import {Component, inject, signal} from '@angular/core';
|
||||
import {TabGroup, TabGroupComponent} from '../../tab-group/tab-group.component';
|
||||
import {ActiveTabDirective} from '../../tab-group/active-tab.directive';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {SubjectListComponent} from '../../subject/subject-list/subject-list.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-tenant-detail',
|
||||
imports: [
|
||||
TabGroupComponent,
|
||||
ActiveTabDirective
|
||||
ActiveTabDirective,
|
||||
SubjectListComponent
|
||||
],
|
||||
templateUrl: './tenant-detail.component.html',
|
||||
styleUrl: './tenant-detail.component.scss',
|
||||
@ -38,13 +40,17 @@ export class TenantDetailComponent {
|
||||
]
|
||||
|
||||
protected activeTabId = signal<string | null>(null)
|
||||
protected tenantId = signal<string | null>(null)
|
||||
|
||||
private route = inject(ActivatedRoute);
|
||||
private router = inject(Router)
|
||||
|
||||
constructor() {
|
||||
const tabId = this.route.snapshot.queryParamMap.get('tab');
|
||||
const routeSnapshot = this.route.snapshot;
|
||||
const tenantId = routeSnapshot.paramMap.get('id');
|
||||
const tabId = routeSnapshot.queryParamMap.get('tab');
|
||||
this.activeTabId.set(tabId)
|
||||
this.tenantId.set(tenantId);
|
||||
}
|
||||
|
||||
async tabChanged(tab: TabGroup) {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import {ResolveFn} from '@angular/router';
|
||||
import {inject} from '@angular/core';
|
||||
import {map} from 'rxjs';
|
||||
import {SubjectService} from '../clients/gandalf/mithrandir/subject.service';
|
||||
import {SubjectService} from '../clients/gandalf/mithrandir/subject/subject.service';
|
||||
|
||||
export const subjectResolver: ResolveFn<string> = (route, state) => {
|
||||
const subjectService = inject(SubjectService);
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
using HashidsNet;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Suspectus.Gandalf.Palantir.Abstractions;
|
||||
using Suspectus.Gandalf.Palantir.Data.Database;
|
||||
using Suspectus.Gandalf.Palantir.Data.Dto.Subject;
|
||||
using Suspectus.Gandalf.Palantir.Data.Entities.Base;
|
||||
|
||||
namespace Suspectus.Gandalf.Palantir.Api.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class SubjectController(InvokerContext invokerContext, ApplicationContext context) : ControllerBase
|
||||
public class SubjectController(InvokerContext invokerContext, ApplicationContext context, IHashids hashids) : ControllerBase
|
||||
{
|
||||
[HttpGet("me")]
|
||||
public async Task<IActionResult> GetSubject()
|
||||
@ -15,4 +18,33 @@ public class SubjectController(InvokerContext invokerContext, ApplicationContext
|
||||
var subject = await context.Subjects.Where(x => x.Id == invokerContext.Invoker!.SubjectId).Select(x => x.Name).SingleAsync();
|
||||
return Ok(new { Name = subject });
|
||||
}
|
||||
|
||||
[HttpGet("for-tenant/{tenantIdHash}")]
|
||||
public async Task<IActionResult> GetSubjectsByTenantId(string tenantIdHash, InvokerContext invokerContext)
|
||||
{
|
||||
if (!hashids.TryDecodeSingleLong(tenantIdHash, out var tenantId))
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
var tenantOwnerId = await context.Tenants.Where(x => x.Id == tenantId).Select(x => (long?)x.OwnerId).SingleOrDefaultAsync();
|
||||
|
||||
if (tenantOwnerId is null)
|
||||
return NotFound();
|
||||
|
||||
var subjects = await context.TenantSubjectRelations
|
||||
.Where(x => x.TenantId == tenantId)
|
||||
.Select(x => x.Subject!)
|
||||
.ToListAsync();
|
||||
|
||||
var dtos = subjects.Select(x => new SubjectListDto
|
||||
{
|
||||
Id = hashids.EncodeLong(x.Id!.Value),
|
||||
Name = x.Name,
|
||||
IsOwner = x.Id == tenantOwnerId,
|
||||
Visibility = x.Visibility
|
||||
});
|
||||
|
||||
return Ok(dtos);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
using Suspectus.Gandalf.Palantir.Data.Entities.Base;
|
||||
|
||||
namespace Suspectus.Gandalf.Palantir.Data.Dto.Subject;
|
||||
|
||||
public class SubjectListDto
|
||||
{
|
||||
public required string Id { get; set; }
|
||||
public required string Name { get; set; }
|
||||
public required bool IsOwner { get; set; }
|
||||
public required EntityVisibility Visibility { get; set; }
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user