MME-34 Remove tanstack from recipe upload page and ai-or-manual step.

This commit is contained in:
Jesse Brault 2026-02-19 18:11:38 -06:00
parent 907c8c34ad
commit e1479f6078
5 changed files with 61 additions and 31 deletions

View File

@ -15,7 +15,6 @@ import { tryMaybeInt } from '../../shared/util';
import { from, map, switchMap, tap } from 'rxjs'; import { from, map, switchMap, tap } from 'rxjs';
import { Review } from './steps/review/review'; import { Review } from './steps/review/review';
import { RecipeEditFormSubmitEvent } from '../../shared/components/recipe-edit-form/RecipeEditFormSubmitEvent'; import { RecipeEditFormSubmitEvent } from '../../shared/components/recipe-edit-form/RecipeEditFormSubmitEvent';
import { QueryClient } from '@tanstack/angular-query-experimental';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
@Component({ @Component({
@ -37,7 +36,6 @@ export class RecipeUploadPage implements OnInit {
private readonly router = inject(Router); private readonly router = inject(Router);
private readonly activatedRoute = inject(ActivatedRoute); private readonly activatedRoute = inject(ActivatedRoute);
private readonly recipeDraftService = inject(RecipeDraftService); private readonly recipeDraftService = inject(RecipeDraftService);
private readonly queryClient = inject(QueryClient);
private readonly toastrService = inject(ToastrService); private readonly toastrService = inject(ToastrService);
private isValidStep(step: number): boolean { private isValidStep(step: number): boolean {
@ -163,9 +161,6 @@ export class RecipeUploadPage implements OnInit {
protected async onDeleteDraft(): Promise<void> { protected async onDeleteDraft(): Promise<void> {
await this.recipeDraftService.deleteDraft(this.model().draft!.id); await this.recipeDraftService.deleteDraft(this.model().draft!.id);
await this.queryClient.invalidateQueries({
queryKey: ['recipe-upload', 'in-progress-drafts'],
});
await this.switchModel( await this.switchModel(
{ {
inProgressStep: RecipeUploadStep.START, inProgressStep: RecipeUploadStep.START,

View File

@ -0,0 +1,18 @@
.in-progress-draft-link,
.in-progress-draft-list-item {
display: flex;
column-gap: 5px;
align-items: baseline;
}
.in-progress-draft-link * {
cursor: pointer;
}
.last-saved {
font-size: 0.8em;
}
.no-title {
font-style: italic;
}

View File

@ -1,19 +1,24 @@
<section> <section>
<h2>Start</h2> <h2>Start</h2>
<section> <section>
@if (inProgressDrafts.isLoading()) { @if (loadingDrafts()) {
<p>Loading drafts...</p> <app-spinner></app-spinner>
} @else if (inProgressDrafts.isError()) { } @else if (drafts().length) {
<p>Could not fetch drafts!</p>
} @else if (inProgressDrafts.isSuccess() && inProgressDrafts.data().length) {
<h3>In Progress Drafts</h3> <h3>In Progress Drafts</h3>
<ul> <ul>
@for (draft of inProgressDrafts.data(); track draft.id) { @for (draft of drafts(); track $index) {
<li> <li class="in-progress-draft-list-item">
<a (click)="onInProgressDraftClick(draft)"> <a class="in-progress-draft-link" (click)="onInProgressDraftClick(draft)">
<fa-icon [icon]="faFilePen"></fa-icon> <fa-icon [icon]="faFilePen"></fa-icon>
{{ draft.title }} @if (draft.title) {
<span>{{ draft.title }}</span>
} @else {
<span class="no-title">(No title)</span>
}
</a> </a>
<span class="last-saved"
>(Last saved {{ (draft.modified ? draft.modified : draft.created) | date: "short" }})</span
>
</li> </li>
} }
</ul> </ul>

View File

@ -1,29 +1,32 @@
import { Component, computed, inject, input, output } from '@angular/core'; import { Component, computed, inject, input, OnInit, output, signal } from '@angular/core';
import { MatButton } from '@angular/material/button'; import { MatButton } from '@angular/material/button';
import { ReactiveFormsModule } from '@angular/forms'; import { ReactiveFormsModule } from '@angular/forms';
import { AIOrManualSubmitEvent } from './AIOrManualSubmitEvent'; import { AIOrManualSubmitEvent } from './AIOrManualSubmitEvent';
import { FileUpload } from '../../../../shared/components/file-upload/file-upload'; import { FileUpload } from '../../../../shared/components/file-upload/file-upload';
import { FileUploadEvent } from '../../../../shared/components/file-upload/FileUploadEvent'; import { FileUploadEvent } from '../../../../shared/components/file-upload/FileUploadEvent';
import { injectQuery } from '@tanstack/angular-query-experimental';
import { RecipeDraftService } from '../../../../shared/services/RecipeDraftService'; import { RecipeDraftService } from '../../../../shared/services/RecipeDraftService';
import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { faFilePen } from '@fortawesome/free-solid-svg-icons'; import { faFilePen } from '@fortawesome/free-solid-svg-icons';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { RecipeDraftViewModel } from '../../../../shared/models/RecipeDraftView.model'; import { RecipeDraftViewModel } from '../../../../shared/models/RecipeDraftView.model';
import { Spinner } from '../../../../shared/components/spinner/spinner';
import { ToastrService } from 'ngx-toastr';
import { DatePipe } from '@angular/common';
@Component({ @Component({
selector: 'app-ai-or-manual', selector: 'app-ai-or-manual',
imports: [MatButton, ReactiveFormsModule, FileUpload, FaIconComponent], imports: [MatButton, ReactiveFormsModule, FileUpload, FaIconComponent, Spinner, DatePipe],
templateUrl: './ai-or-manual.html', templateUrl: './ai-or-manual.html',
styleUrl: './ai-or-manual.css', styleUrl: './ai-or-manual.css',
}) })
export class AiOrManual { export class AiOrManual implements OnInit {
public sourceFile = input.required<File | null>(); public sourceFile = input.required<File | null>();
public sourceFileChange = output<FileUploadEvent>(); public sourceFileChange = output<FileUploadEvent>();
public submitStep = output<AIOrManualSubmitEvent>(); public submitStep = output<AIOrManualSubmitEvent>();
private readonly recipeDraftService = inject(RecipeDraftService); private readonly recipeDraftService = inject(RecipeDraftService);
private readonly router = inject(Router); private readonly router = inject(Router);
private readonly toastrService = inject(ToastrService);
protected readonly sourceFilesArray = computed(() => { protected readonly sourceFilesArray = computed(() => {
const maybeSourceFile = this.sourceFile(); const maybeSourceFile = this.sourceFile();
@ -34,12 +37,23 @@ export class AiOrManual {
} }
}); });
protected readonly inProgressDrafts = injectQuery(() => ({ protected readonly loadingDrafts = signal(false);
queryKey: ['recipe-upload', 'in-progress-drafts'], protected readonly drafts = signal<RecipeDraftViewModel[]>([]);
queryFn: () => {
return this.recipeDraftService.getInProgressDrafts(); public ngOnInit(): void {
}, this.loadingDrafts.set(true);
})); this.recipeDraftService.getInProgressDrafts().subscribe({
next: (drafts) => {
this.loadingDrafts.set(false);
this.drafts.set(drafts);
},
error: (e) => {
this.loadingDrafts.set(false);
console.error(e);
this.toastrService.error('There was an error Recipe drafts');
},
});
}
protected onFileChange(event: FileUploadEvent) { protected onFileChange(event: FileUploadEvent) {
this.sourceFileChange.emit(event); this.sourceFileChange.emit(event);

View File

@ -34,13 +34,11 @@ export class RecipeDraftService {
}; };
} }
public getInProgressDrafts(): Promise<RecipeDraftViewModel[]> { public getInProgressDrafts(): Observable<RecipeDraftViewModel[]> {
return firstValueFrom( return this.http.get<WithStringDates<RecipeDraftViewModel>[]>(this.endpointService.getUrl('recipeDrafts')).pipe(
this.http.get<WithStringDates<RecipeDraftViewModel>[]>(this.endpointService.getUrl('recipeDrafts')).pipe( map((rawViews) => {
map((rawViews) => { return rawViews.map((rawView) => this.hydrateView(rawView));
return rawViews.map((rawView) => this.hydrateView(rawView)); }),
}),
),
); );
} }