Skip to content

Commit

Permalink
Merge pull request #261 from e-picsa/feat/resources-refactor
Browse files Browse the repository at this point in the history
Feat(dashboard): resources system improvements
  • Loading branch information
chrismclarke authored Apr 8, 2024
2 parents 026e4f9 + 772b75b commit 4853c41
Show file tree
Hide file tree
Showing 82 changed files with 1,673 additions and 243 deletions.
5 changes: 3 additions & 2 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ apps/_deprecated
apps/picsa-apps/extension-app-native
# Invalid token versions
apps/picsa-server/supabase/types/index.ts
# Generated file
apps/picsa-apps/dashboard/src/app/modules/climate/types/api.d.ts
# Generated files
apps/picsa-apps/dashboard/src/app/modules/climate/types/api.d.ts
libs/webcomponents/src/components.d.ts
6 changes: 3 additions & 3 deletions apps/picsa-apps/dashboard/src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { NxWelcomeComponent } from './nx-welcome.component';
import { RouterTestingModule } from '@angular/router/testing';

import { AppComponent } from './app.component';

describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AppComponent, NxWelcomeComponent, RouterTestingModule],
imports: [AppComponent, RouterTestingModule],
}).compileComponents();
});

Expand Down
14 changes: 14 additions & 0 deletions apps/picsa-apps/dashboard/src/app/data/navLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ export const DASHBOARD_NAV_LINKS: INavLink[] = [
label: 'Resources',
href: '/resources',
matIcon: 'library_books',
children: [
{
label: 'Collections',
href: '/collections',
},
{
label: 'Files',
href: '/files',
},
{
label: 'Links',
href: '/links',
},
],
},
{
label: 'Climate',
Expand Down
20 changes: 18 additions & 2 deletions apps/picsa-apps/dashboard/src/app/material.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { MatButtonModule } from '@angular/material/button';
import { MatChipsModule } from '@angular/material/chips';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatIconModule, MatIconRegistry } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatListModule } from '@angular/material/list';
import { MatRadioModule } from '@angular/material/radio';
Expand All @@ -12,6 +12,8 @@ import { MatStepperModule } from '@angular/material/stepper';
import { MatTableModule } from '@angular/material/table';
import { MatTabsModule } from '@angular/material/tabs';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatTooltipModule } from '@angular/material/tooltip';
import { DomSanitizer } from '@angular/platform-browser';

const matModules = [
MatButtonModule,
Expand All @@ -27,10 +29,24 @@ const matModules = [
MatTableModule,
MatTabsModule,
MatToolbarModule,
MatTooltipModule,
];

@NgModule({
imports: matModules,
exports: matModules,
})
export class DashboardMaterialModule {}
export class DashboardMaterialModule {
constructor(private matIconRegistry: MatIconRegistry, private domSanitizer: DomSanitizer) {
this.registerIcons();
}

registerIcons() {
// TODO - filetype icons are duplicated from frontend resources module, could be shared as common icon set
const icons = ['filetype_document', 'filetype_pdf', 'filetype_video', 'play_store'];
for (const icon of icons) {
const url = this.domSanitizer.bypassSecurityTrustResourceUrl(`assets/mat-icons/${icon}.svg`);
this.matIconRegistry.addSvgIconInNamespace('dashboard', icon, url);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<div class="page-content">
<div style="display: flex; align-items: center">
<h2>Resource Collections</h2>
<button
mat-stroked-button
color="primary"
routerLink="create"
style="margin-top: 1rem; margin-left: auto"
[disabled]="true"
>
<mat-icon>add</mat-icon>
Add Collection
</button>
</div>
<p>
Here you can find resources that have been grouped to form a collection.
<br />
Collections can also contain nested sub-collections
</p>

<picsa-data-table
[data]="collections"
[options]="tableOptions"
[valueTemplates]="{
modified_at: modifiedAtTemplate,
cover_image: coverImageTemplate,
collection_parent: collectionParentTemplate
}"
>
<!-- cover image -->
<ng-template #coverImageTemplate let-coverImage>
<img class="cover-image" [src]="coverImage | storagePath" />
</ng-template>
<!-- parent collection -->
<ng-template #collectionParentTemplate let-collection>
@if(collection){
<img
class="cover-image color-on-hover"
[src]="collection.cover_image | storagePath"
[matTooltip]="collection.title"
/>
}
</ng-template>

<!-- modified date -->
<ng-template #modifiedAtTemplate let-date>
{{ date | date: 'mediumDate' }}
</ng-template></picsa-data-table
>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.cover-image {
width: 40px;
height: 40px;
object-fit: cover;
display: block;
margin: auto;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { ResourceCollectionsComponent } from './resource-collections.component';

describe('ResourceCollectionsComponent', () => {
let component: ResourceCollectionsComponent;
let fixture: ComponentFixture<ResourceCollectionsComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ResourceCollectionsComponent],
}).compileComponents();

fixture = TestBed.createComponent(ResourceCollectionsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { CommonModule } from '@angular/common';
import { Component, effect, OnInit } from '@angular/core';
import { RouterModule } from '@angular/router';
import { formatHeaderDefault, IDataTableOptions, PicsaDataTableComponent } from '@picsa/shared/features';
import { StoragePathPipe } from '@picsa/shared/services/core/supabase';

import { DashboardMaterialModule } from '../../../../material.module';
import { ResourcesDashboardService } from '../../resources.service';
import { IResourceCollectionRow } from '../../types';

interface IMergedCollection extends Omit<IResourceCollectionRow, 'collection_parent'> {
collection_parent?: IResourceCollectionRow;
}

const DISPLAY_COLUMNS: (keyof IMergedCollection)[] = [
'cover_image',
'title',
'description',
// 'resource_collections',
// 'resource_files',
// 'resource_links',
'collection_parent',
// 'sort_order',
'modified_at',
];

@Component({
selector: 'dashboard-resource-collections',
standalone: true,
imports: [
CommonModule,
CommonModule,
DashboardMaterialModule,
PicsaDataTableComponent,
RouterModule,
StoragePathPipe,
],
templateUrl: './resource-collections.component.html',
styleUrl: './resource-collections.component.scss',
})
export class ResourceCollectionsComponent implements OnInit {
public collections: IMergedCollection[] = [];
public tableOptions: IDataTableOptions = {
displayColumns: DISPLAY_COLUMNS,
formatHeader: (v) => {
switch (v as keyof IMergedCollection) {
case 'resource_collections':
return 'Child Collections';
case 'resource_files':
return 'Files';
case 'resource_links':
return 'Links';
case 'collection_parent':
return 'Parent Collection';
default:
return formatHeaderDefault(v);
}
},
};

constructor(private service: ResourcesDashboardService) {
effect(() => {
const collectionsHashmap = this.service.collectionsById();
const collections = service.collections().map((c) => this.mergeCollectionData(c, collectionsHashmap));
this.collections = collections.sort(this.sortCollections);
});
}
async ngOnInit() {
await this.service.ready();
}

private mergeCollectionData(
collection: IResourceCollectionRow,
collectionsHashmap: Record<string, IResourceCollectionRow>
): IMergedCollection {
return {
...collection,
collection_parent: collectionsHashmap[collection.collection_parent || ''],
};
}

/** Sort collections first by name of parent (root collections first), then by modified date */
private sortCollections(a: IMergedCollection, b: IMergedCollection) {
if (a.collection_parent === b.collection_parent) {
return b.modified_at > a.modified_at ? 1 : -1;
}
return (b.collection_parent || '') > (a.collection_parent || '') ? -1 : 1;
}

private getCollectionTree(collections: IResourceCollectionRow[]) {
const nodes = {};

const children = {};
for (const { collection_parent } of collections) {
if (collection_parent) {
if (!children[collection_parent]) children[collection_parent] = [];
children[collection_parent].push(collections);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { IStorageEntry } from '@picsa/shared/services/core/supabase/services/sup
import { DashboardMaterialModule } from '../../../../material.module';
import { DashboardResourcesStorageLinkComponent } from '../../components/storage-link/storage-link.component';
import { ResourcesDashboardService } from '../../resources.service';
import { IResourceRow } from '../../types';
import { IResourceFileRow } from '../../types';

@Component({
selector: 'dashboard-resource-create',
Expand Down Expand Up @@ -64,7 +64,7 @@ export class ResourceCreateComponent implements OnInit {
await this.service.ready();
const { id } = this.route.snapshot.params;
if (id) {
const { data } = await this.service.table.select<'*', IResourceRow>('*').eq('id', id);
const { data } = await this.service.tables.files.select<'*', IResourceFileRow>('*').eq('id', id);
const resource = data?.[0];
if (resource) {
this.populateResource(resource);
Expand All @@ -79,23 +79,23 @@ export class ResourceCreateComponent implements OnInit {
if (values.id === null) {
delete values.id;
}
const { data, error } = await this.service.table.upsert(values);
const { data, error } = await this.service.tables.files.upsert(values);
console.log({ data, error });
}

private populateResource(resource: IResourceRow) {
this.resourceType = resource.type as any;
console.log('populate resource', resource);
switch (resource.type) {
case 'file':
this.fileForm.patchValue(resource);
break;
case 'link':
this.linkForm.patchValue(resource);
break;
default:
console.warn('Resource type not supported', resource.type);
}
private populateResource(resource: any) {
// this.resourceType = resource.type as any;
// console.log('populate resource', resource);
// switch (resource.type) {
// case 'file':
// this.fileForm.patchValue(resource);
// break;
// case 'link':
// this.linkForm.patchValue(resource);
// break;
// default:
// console.warn('Resource type not supported', resource.type);
// }
}

public async handleUploadComplete(res: IUploadResult[], controlName: 'storage_file' | 'storage_cover') {
Expand Down
Loading

0 comments on commit 4853c41

Please sign in to comment.