@rolatech/angular-community 20.2.9-beta.7 → 20.3.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/rolatech-angular-community-community-details.component-B4kDYAna.mjs +46 -0
- package/fesm2022/rolatech-angular-community-community-details.component-B4kDYAna.mjs.map +1 -0
- package/fesm2022/rolatech-angular-community-community-index.component-CJ_ddZSo.mjs +65 -0
- package/fesm2022/rolatech-angular-community-community-index.component-CJ_ddZSo.mjs.map +1 -0
- package/fesm2022/rolatech-angular-community-community-manage-index.component-CQYy_iI7.mjs +68 -0
- package/fesm2022/rolatech-angular-community-community-manage-index.component-CQYy_iI7.mjs.map +1 -0
- package/fesm2022/rolatech-angular-community-community-manage-post-details.component-B70ewcPc.mjs +70 -0
- package/fesm2022/rolatech-angular-community-community-manage-post-details.component-B70ewcPc.mjs.map +1 -0
- package/fesm2022/rolatech-angular-community-community-manage-post.component-DVRDQzXA.mjs +137 -0
- package/fesm2022/rolatech-angular-community-community-manage-post.component-DVRDQzXA.mjs.map +1 -0
- package/fesm2022/rolatech-angular-community-rolatech-angular-community-40yr8v0S.mjs +500 -0
- package/fesm2022/rolatech-angular-community-rolatech-angular-community-40yr8v0S.mjs.map +1 -0
- package/fesm2022/rolatech-angular-community.mjs +1 -112
- package/fesm2022/rolatech-angular-community.mjs.map +1 -1
- package/package.json +13 -5
- package/types/rolatech-angular-community.d.ts +44 -2
- package/fesm2022/rolatech-angular-community-community-index.component-DI3vIua7.mjs +0 -15
- package/fesm2022/rolatech-angular-community-community-index.component-DI3vIua7.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rolatech-angular-community-community-manage-post.component-DVRDQzXA.mjs","sources":["../../../../packages/angular-community/src/lib/pages/community-manage/community-manage-post/community-manage-post.component.ts","../../../../packages/angular-community/src/lib/pages/community-manage/community-manage-post/community-manage-post.component.html"],"sourcesContent":["import { CommonModule } from '@angular/common';\nimport { Component, computed, inject, model, signal } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatIconModule } from '@angular/material/icon';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { TabComponent, TabsComponent } from '@rolatech/angular-components';\nimport { SavePostRequest, SnackBarService, TitleService } from '@rolatech/angular-services';\nimport { CommunityPostStoreService } from '../../../services/community-post-store.service';\nimport { createPostPreview, toTagInput } from '../../../utils/post-view.util';\nimport { startWith } from 'rxjs';\n\n@Component({\n selector: 'rolatech-community-manage-post',\n imports: [CommonModule, ReactiveFormsModule, MatButtonModule, MatIconModule, TabsComponent, TabComponent],\n templateUrl: './community-manage-post.component.html',\n styleUrl: './community-manage-post.component.scss',\n})\nexport class CommunityManagePostComponent {\n private readonly formBuilder = inject(FormBuilder);\n private readonly route = inject(ActivatedRoute);\n private readonly router = inject(Router);\n private readonly postStore = inject(CommunityPostStoreService);\n private readonly snackBar = inject(SnackBarService);\n private readonly titleService = inject(TitleService);\n\n readonly editorTab = model(0);\n readonly loading = signal(false);\n readonly savingDraft = signal(false);\n readonly publishing = signal(false);\n readonly postId = signal<string | null>(this.route.snapshot.paramMap.get('id'));\n\n readonly form = this.formBuilder.nonNullable.group({\n title: ['', [Validators.required, Validators.minLength(4)]],\n slug: [''],\n summary: [''],\n tags: [''],\n coverImage: [''],\n content: ['', [Validators.required, Validators.minLength(20)]],\n });\n\n private readonly formValue = toSignal(this.form.valueChanges.pipe(startWith(this.form.getRawValue())), {\n initialValue: this.form.getRawValue(),\n });\n\n readonly preview = computed(() => createPostPreview(this.formValue().content ?? ''));\n readonly canSubmit = computed(\n () => (this.formValue().title?.trim().length ?? 0) >= 4 && (this.formValue().content?.trim().length ?? 0) >= 20,\n );\n\n constructor() {\n this.titleService.setTitle(this.postId() ? 'Edit post | Community' : 'Write post | Community');\n if (this.postId()) {\n this.loadPost(this.postId() as string);\n }\n }\n\n saveDraft() {\n if (!this.canSubmit()) {\n this.form.markAllAsTouched();\n return;\n }\n\n this.savingDraft.set(true);\n this.postStore.saveDraft({ id: this.postId() ?? undefined, ...this.payload() }).subscribe({\n next: (post) => {\n this.postId.set(post.id);\n this.form.patchValue({ slug: post.slug ?? '', tags: toTagInput(post.tags) }, { emitEvent: false });\n this.savingDraft.set(false);\n this.snackBar.open('Draft saved');\n this.navigateToEditor(post.id);\n },\n error: () => {\n this.savingDraft.set(false);\n },\n });\n }\n\n publish() {\n if (!this.canSubmit()) {\n this.form.markAllAsTouched();\n return;\n }\n\n this.publishing.set(true);\n this.postStore.publish({ id: this.postId() ?? undefined, ...this.payload() }).subscribe({\n next: (post) => {\n this.postId.set(post.id);\n this.publishing.set(false);\n this.snackBar.open('Post published');\n this.navigateToPreview(post.id);\n },\n error: () => {\n this.publishing.set(false);\n },\n });\n }\n\n private loadPost(id: string) {\n this.loading.set(true);\n this.postStore.getManagePost(id).subscribe({\n next: (post) => {\n if (post) {\n this.form.patchValue({\n title: post.title,\n slug: post.slug ?? '',\n summary: post.summary ?? post.excerpt ?? '',\n tags: toTagInput(post.tags),\n coverImage: post.coverImage ?? post.cover ?? '',\n content: post.content,\n });\n }\n\n this.loading.set(false);\n },\n error: () => {\n this.loading.set(false);\n },\n });\n }\n\n private payload(): SavePostRequest {\n const value = this.form.getRawValue();\n return {\n title: value.title,\n slug: value.slug,\n summary: value.summary,\n coverImage: value.coverImage,\n tags: value.tags\n .split(',')\n .map((tag) => tag.trim())\n .filter(Boolean),\n content: value.content,\n };\n }\n\n private navigateToEditor(id: string) {\n const parent = this.route.parent ?? this.route;\n this.router.navigate(['manage', id, 'edit'], { relativeTo: parent });\n }\n\n private navigateToPreview(id: string) {\n const parent = this.route.parent ?? this.route;\n this.router.navigate(['manage', id], { relativeTo: parent });\n }\n}\n","<div class=\"community-manage-post\">\n <section class=\"community-manage-post__header\">\n <div>\n <h2>{{ postId() ? 'Edit post' : 'Create post' }}</h2>\n <p>Write in markdown, check the preview, then save a durable draft or publish immediately.</p>\n </div>\n\n <div class=\"community-manage-post__actions\">\n <button mat-stroked-button type=\"button\" (click)=\"saveDraft()\" [disabled]=\"savingDraft() || publishing()\">\n <mat-icon>save</mat-icon>\n <span>{{ savingDraft() ? 'Saving...' : 'Save draft' }}</span>\n </button>\n\n <button mat-flat-button color=\"primary\" type=\"button\" (click)=\"publish()\" [disabled]=\"savingDraft() || publishing()\">\n <mat-icon>publish</mat-icon>\n <span>{{ publishing() ? 'Publishing...' : 'Publish' }}</span>\n </button>\n </div>\n </section>\n\n @if (loading()) {\n <div class=\"community-manage-post__state\">Loading post...</div>\n } @else {\n <section class=\"community-manage-post__workspace\">\n <form class=\"community-manage-post__form\" [formGroup]=\"form\">\n <label>\n <span>Title</span>\n <input type=\"text\" formControlName=\"title\" placeholder=\"Write a clear, scannable title\" />\n </label>\n\n <label>\n <span>Slug</span>\n <input type=\"text\" formControlName=\"slug\" placeholder=\"url-ready-post-slug\" />\n </label>\n\n <label>\n <span>Summary</span>\n <textarea rows=\"3\" formControlName=\"summary\" placeholder=\"Short summary shown in cards and previews\"></textarea>\n </label>\n\n <label>\n <span>Tags</span>\n <input type=\"text\" formControlName=\"tags\" placeholder=\"angular, community, design\" />\n </label>\n\n <label>\n <span>Cover image URL</span>\n <input type=\"url\" formControlName=\"coverImage\" placeholder=\"https://...\" />\n </label>\n\n <label>\n <span>Content</span>\n <textarea rows=\"18\" formControlName=\"content\" placeholder=\"# Heading Write markdown content here...\"></textarea>\n </label>\n </form>\n\n <aside class=\"community-manage-post__preview\">\n <rolatech-tabs mode=\"background\" [(select)]=\"editorTab\">\n <rolatech-tab label=\"Preview\"></rolatech-tab>\n <rolatech-tab label=\"Stats\"></rolatech-tab>\n </rolatech-tabs>\n\n @if (editorTab() === 0) {\n <article class=\"community-manage-post__preview-card\">\n <h3>{{ form.controls.title.value || 'Untitled post' }}</h3>\n <p>{{ form.controls.summary.value || 'Add a summary so cards and detail pages have useful context.' }}</p>\n <div class=\"community-manage-post__preview-content markdown-body\" [innerHTML]=\"preview().html\"></div>\n </article>\n } @else {\n <article class=\"community-manage-post__stats-card\">\n <div>\n <span class=\"community-manage-post__stat-label\">Words</span>\n <strong>{{ preview().wordCount }}</strong>\n </div>\n <div>\n <span class=\"community-manage-post__stat-label\">Reading time</span>\n <strong>{{ preview().readingTimeMinutes }} min</strong>\n </div>\n <div>\n <span class=\"community-manage-post__stat-label\">Slug</span>\n <strong>{{ form.controls.slug.value || 'Generated on save' }}</strong>\n </div>\n </article>\n }\n </aside>\n </section>\n }\n</div>\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
import { isPlatformBrowser, CommonModule } from '@angular/common';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { inject, DestroyRef, PLATFORM_ID, model, signal, Component, Injectable } from '@angular/core';
|
|
4
|
+
import * as i2 from '@angular/material/button';
|
|
5
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
6
|
+
import * as i3 from '@angular/material/icon';
|
|
7
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
8
|
+
import { Router, NavigationEnd, RouterLink, RouterOutlet } from '@angular/router';
|
|
9
|
+
import { TabsComponent, TabComponent } from '@rolatech/angular-components';
|
|
10
|
+
import { ThemeService, TitleService, BaseService, PostService } from '@rolatech/angular-services';
|
|
11
|
+
import { filter, take, map, catchError, of } from 'rxjs';
|
|
12
|
+
import { marked } from 'marked';
|
|
13
|
+
|
|
14
|
+
class CommunityLayoutComponent {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.router = inject(Router);
|
|
17
|
+
this.destroyRef = inject(DestroyRef);
|
|
18
|
+
this.platformId = inject(PLATFORM_ID);
|
|
19
|
+
this.themeService = inject(ThemeService);
|
|
20
|
+
this.titleService = inject(TitleService);
|
|
21
|
+
this.themeStorageKey = 'rolatech-community-theme';
|
|
22
|
+
this.navIndex = model(0, ...(ngDevMode ? [{ debugName: "navIndex" }] : []));
|
|
23
|
+
this.theme = signal('light', ...(ngDevMode ? [{ debugName: "theme" }] : []));
|
|
24
|
+
this.titleService.setTitle('Community');
|
|
25
|
+
this.syncTheme();
|
|
26
|
+
this.updateNavIndex(this.router.url);
|
|
27
|
+
const sub = this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe((event) => {
|
|
28
|
+
this.updateNavIndex(event.urlAfterRedirects);
|
|
29
|
+
});
|
|
30
|
+
this.destroyRef.onDestroy(() => sub.unsubscribe());
|
|
31
|
+
}
|
|
32
|
+
toggleTheme() {
|
|
33
|
+
const nextTheme = this.theme() === 'dark' ? 'light' : 'dark';
|
|
34
|
+
this.theme.set(nextTheme);
|
|
35
|
+
this.themeService.setDarkTheme(nextTheme === 'dark');
|
|
36
|
+
if (isPlatformBrowser(this.platformId)) {
|
|
37
|
+
localStorage.setItem(this.themeStorageKey, nextTheme);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
syncTheme() {
|
|
41
|
+
if (isPlatformBrowser(this.platformId)) {
|
|
42
|
+
const saved = localStorage.getItem(this.themeStorageKey);
|
|
43
|
+
if (saved === 'light' || saved === 'dark') {
|
|
44
|
+
this.theme.set(saved);
|
|
45
|
+
this.themeService.setDarkTheme(saved === 'dark');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
this.themeService.isDarkTheme.pipe(take(1)).subscribe((isDark) => {
|
|
50
|
+
this.theme.set(isDark ? 'dark' : 'light');
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
updateNavIndex(url) {
|
|
54
|
+
if (url.includes('/manage/create')) {
|
|
55
|
+
this.navIndex.set(2);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (url.includes('/manage')) {
|
|
59
|
+
this.navIndex.set(1);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
this.navIndex.set(0);
|
|
63
|
+
}
|
|
64
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: CommunityLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
65
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.1", type: CommunityLayoutComponent, isStandalone: true, selector: "rolatech-community-layout", inputs: { navIndex: { classPropertyName: "navIndex", publicName: "navIndex", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { navIndex: "navIndexChange" }, ngImport: i0, template: "<div class=\"community-shell\" [attr.data-theme]=\"theme()\">\n <div class=\"community-shell__orb community-shell__orb--primary\"></div>\n <div class=\"community-shell__orb community-shell__orb--secondary\"></div>\n\n <header class=\"community-shell__header\">\n <div class=\"community-shell__brand\">\n <div class=\"community-shell__eyebrow\">RolaTech Community</div>\n <h1>Shipping notes, drafts, and previews in one place.</h1>\n <p>\n Explore published posts, keep draft work moving, and switch between editor and preview without leaving the route\n tree.\n </p>\n </div>\n\n <button mat-stroked-button type=\"button\" class=\"community-shell__theme-button\" (click)=\"toggleTheme()\">\n <mat-icon>{{ theme() === 'dark' ? 'light_mode' : 'dark_mode' }}</mat-icon>\n <span>{{ theme() === 'dark' ? 'Light theme' : 'Dark theme' }}</span>\n </button>\n </header>\n\n <section class=\"community-shell__hero\">\n <div class=\"community-shell__hero-copy\">\n <span class=\"community-shell__pill\">Automatic routes</span>\n <span class=\"community-shell__pill\">Draft-safe workflow</span>\n <span class=\"community-shell__pill\">Markdown preview</span>\n </div>\n\n <div class=\"community-shell__hero-card\">\n <div class=\"community-shell__metric\">\n <span class=\"community-shell__metric-value\">3</span>\n <span class=\"community-shell__metric-label\">Primary routes wired in-package</span>\n </div>\n <div class=\"community-shell__metric\">\n <span class=\"community-shell__metric-value\">2</span>\n <span class=\"community-shell__metric-label\">Tab appearances from `angular-components`</span>\n </div>\n </div>\n </section>\n\n <rolatech-tabs mode=\"background\" [(select)]=\"navIndex\">\n <rolatech-tab class=\"cursor-pointer\" label=\"Explore\" [routerLink]=\"['./']\"></rolatech-tab>\n <rolatech-tab class=\"cursor-pointer\" label=\"Manage\" [routerLink]=\"['./manage']\"></rolatech-tab>\n <rolatech-tab class=\"cursor-pointer\" label=\"Write\" [routerLink]=\"['./manage/create']\"></rolatech-tab>\n </rolatech-tabs>\n\n <section class=\"community-shell__content\">\n <router-outlet></router-outlet>\n </section>\n</div>\n", styles: [":host{display:block;min-height:100dvh}.community-shell{--community-background: linear-gradient(180deg, #f8fafc 0%, #eef2ff 42%, #f8fafc 100%);--community-surface: rgba(255, 255, 255, .82);--community-surface-strong: rgba(255, 255, 255, .94);--community-border: rgba(148, 163, 184, .22);--community-text: #0f172a;--community-muted: #475569;--community-shadow: 0 24px 80px rgba(15, 23, 42, .08);min-height:100dvh;padding:clamp(1.25rem,3vw,2.5rem);background:var(--community-background);color:var(--community-text);position:relative;overflow:hidden}.community-shell[data-theme=dark]{--community-background: radial-gradient(circle at top, rgba(59, 130, 246, .16), transparent 34%), linear-gradient(180deg, #020617 0%, #0f172a 46%, #020617 100%);--community-surface: rgba(15, 23, 42, .72);--community-surface-strong: rgba(15, 23, 42, .92);--community-border: rgba(148, 163, 184, .18);--community-text: #e2e8f0;--community-muted: #94a3b8;--community-shadow: 0 24px 80px rgba(2, 6, 23, .42)}.community-shell__orb{position:absolute;border-radius:999px;filter:blur(40px);pointer-events:none}.community-shell__orb--primary{top:-2rem;right:-3rem;width:18rem;height:18rem;background:#0ea5e938}.community-shell__orb--secondary{left:-4rem;bottom:12rem;width:20rem;height:20rem;background:#3b82f62e}.community-shell__header,.community-shell__hero,.community-shell__content,rolatech-tabs{position:relative;z-index:1}.community-shell__header{display:flex;justify-content:space-between;align-items:flex-start;gap:1.5rem;margin-bottom:1.5rem}.community-shell__brand{max-width:42rem}.community-shell__eyebrow{margin-bottom:.6rem;font-size:.8rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:#0284c7}.community-shell__brand h1{margin:0;font-size:clamp(2rem,5vw,3.5rem);line-height:.98;letter-spacing:-.04em}.community-shell__brand p{margin:1rem 0 0;max-width:36rem;color:var(--community-muted);line-height:1.7}.community-shell__theme-button{flex:0 0 auto;border-color:var(--community-border)!important;color:var(--community-text)!important;background:var(--community-surface)!important;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.community-shell__theme-button mat-icon{margin-right:.35rem}.community-shell__hero{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(18rem,1fr);gap:1.25rem;margin-bottom:1.5rem}.community-shell__hero-copy,.community-shell__hero-card,.community-shell__content{border:1px solid var(--community-border);background:var(--community-surface);box-shadow:var(--community-shadow);-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.community-shell__hero-copy{display:flex;flex-wrap:wrap;gap:.75rem;align-content:start;padding:1rem;border-radius:24px}.community-shell__pill{display:inline-flex;align-items:center;min-height:2.25rem;padding:0 .9rem;border-radius:999px;background:#0ea5e91f;color:#0369a1;font-size:.92rem;font-weight:600}.community-shell[data-theme=dark] .community-shell__pill{background:#0ea5e92e;color:#bae6fd}.community-shell__hero-card{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1rem;padding:1.1rem;border-radius:24px}.community-shell__metric{display:grid;gap:.35rem}.community-shell__metric-value{font-size:2rem;font-weight:700;letter-spacing:-.04em}.community-shell__metric-label{color:var(--community-muted);line-height:1.5}.community-shell :is(rolatech-tabs){--rt-tabs-surface: rgba(148, 163, 184, .12);--rt-tab-selected-background: #0284c7;--rt-tab-hover-background-color: rgba(148, 163, 184, .12);margin-bottom:1.5rem}.community-shell__content{border-radius:32px;padding:clamp(1rem,2vw,1.5rem)}@media(max-width:840px){.community-shell__header,.community-shell__hero{grid-template-columns:1fr;display:grid}.community-shell__theme-button{width:fit-content}.community-shell__hero-card{grid-template-columns:1fr}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: TabsComponent, selector: "rolatech-tabs", inputs: ["select", "loading", "block", "mode"], outputs: ["selectChange"] }, { kind: "component", type: TabComponent, selector: "rolatech-tab", inputs: ["label"], outputs: ["selectRequested"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] }); }
|
|
66
|
+
}
|
|
67
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: CommunityLayoutComponent, decorators: [{
|
|
68
|
+
type: Component,
|
|
69
|
+
args: [{ selector: 'rolatech-community-layout', imports: [CommonModule, RouterLink, RouterOutlet, TabsComponent, TabComponent, MatButtonModule, MatIconModule], template: "<div class=\"community-shell\" [attr.data-theme]=\"theme()\">\n <div class=\"community-shell__orb community-shell__orb--primary\"></div>\n <div class=\"community-shell__orb community-shell__orb--secondary\"></div>\n\n <header class=\"community-shell__header\">\n <div class=\"community-shell__brand\">\n <div class=\"community-shell__eyebrow\">RolaTech Community</div>\n <h1>Shipping notes, drafts, and previews in one place.</h1>\n <p>\n Explore published posts, keep draft work moving, and switch between editor and preview without leaving the route\n tree.\n </p>\n </div>\n\n <button mat-stroked-button type=\"button\" class=\"community-shell__theme-button\" (click)=\"toggleTheme()\">\n <mat-icon>{{ theme() === 'dark' ? 'light_mode' : 'dark_mode' }}</mat-icon>\n <span>{{ theme() === 'dark' ? 'Light theme' : 'Dark theme' }}</span>\n </button>\n </header>\n\n <section class=\"community-shell__hero\">\n <div class=\"community-shell__hero-copy\">\n <span class=\"community-shell__pill\">Automatic routes</span>\n <span class=\"community-shell__pill\">Draft-safe workflow</span>\n <span class=\"community-shell__pill\">Markdown preview</span>\n </div>\n\n <div class=\"community-shell__hero-card\">\n <div class=\"community-shell__metric\">\n <span class=\"community-shell__metric-value\">3</span>\n <span class=\"community-shell__metric-label\">Primary routes wired in-package</span>\n </div>\n <div class=\"community-shell__metric\">\n <span class=\"community-shell__metric-value\">2</span>\n <span class=\"community-shell__metric-label\">Tab appearances from `angular-components`</span>\n </div>\n </div>\n </section>\n\n <rolatech-tabs mode=\"background\" [(select)]=\"navIndex\">\n <rolatech-tab class=\"cursor-pointer\" label=\"Explore\" [routerLink]=\"['./']\"></rolatech-tab>\n <rolatech-tab class=\"cursor-pointer\" label=\"Manage\" [routerLink]=\"['./manage']\"></rolatech-tab>\n <rolatech-tab class=\"cursor-pointer\" label=\"Write\" [routerLink]=\"['./manage/create']\"></rolatech-tab>\n </rolatech-tabs>\n\n <section class=\"community-shell__content\">\n <router-outlet></router-outlet>\n </section>\n</div>\n", styles: [":host{display:block;min-height:100dvh}.community-shell{--community-background: linear-gradient(180deg, #f8fafc 0%, #eef2ff 42%, #f8fafc 100%);--community-surface: rgba(255, 255, 255, .82);--community-surface-strong: rgba(255, 255, 255, .94);--community-border: rgba(148, 163, 184, .22);--community-text: #0f172a;--community-muted: #475569;--community-shadow: 0 24px 80px rgba(15, 23, 42, .08);min-height:100dvh;padding:clamp(1.25rem,3vw,2.5rem);background:var(--community-background);color:var(--community-text);position:relative;overflow:hidden}.community-shell[data-theme=dark]{--community-background: radial-gradient(circle at top, rgba(59, 130, 246, .16), transparent 34%), linear-gradient(180deg, #020617 0%, #0f172a 46%, #020617 100%);--community-surface: rgba(15, 23, 42, .72);--community-surface-strong: rgba(15, 23, 42, .92);--community-border: rgba(148, 163, 184, .18);--community-text: #e2e8f0;--community-muted: #94a3b8;--community-shadow: 0 24px 80px rgba(2, 6, 23, .42)}.community-shell__orb{position:absolute;border-radius:999px;filter:blur(40px);pointer-events:none}.community-shell__orb--primary{top:-2rem;right:-3rem;width:18rem;height:18rem;background:#0ea5e938}.community-shell__orb--secondary{left:-4rem;bottom:12rem;width:20rem;height:20rem;background:#3b82f62e}.community-shell__header,.community-shell__hero,.community-shell__content,rolatech-tabs{position:relative;z-index:1}.community-shell__header{display:flex;justify-content:space-between;align-items:flex-start;gap:1.5rem;margin-bottom:1.5rem}.community-shell__brand{max-width:42rem}.community-shell__eyebrow{margin-bottom:.6rem;font-size:.8rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:#0284c7}.community-shell__brand h1{margin:0;font-size:clamp(2rem,5vw,3.5rem);line-height:.98;letter-spacing:-.04em}.community-shell__brand p{margin:1rem 0 0;max-width:36rem;color:var(--community-muted);line-height:1.7}.community-shell__theme-button{flex:0 0 auto;border-color:var(--community-border)!important;color:var(--community-text)!important;background:var(--community-surface)!important;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.community-shell__theme-button mat-icon{margin-right:.35rem}.community-shell__hero{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(18rem,1fr);gap:1.25rem;margin-bottom:1.5rem}.community-shell__hero-copy,.community-shell__hero-card,.community-shell__content{border:1px solid var(--community-border);background:var(--community-surface);box-shadow:var(--community-shadow);-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.community-shell__hero-copy{display:flex;flex-wrap:wrap;gap:.75rem;align-content:start;padding:1rem;border-radius:24px}.community-shell__pill{display:inline-flex;align-items:center;min-height:2.25rem;padding:0 .9rem;border-radius:999px;background:#0ea5e91f;color:#0369a1;font-size:.92rem;font-weight:600}.community-shell[data-theme=dark] .community-shell__pill{background:#0ea5e92e;color:#bae6fd}.community-shell__hero-card{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1rem;padding:1.1rem;border-radius:24px}.community-shell__metric{display:grid;gap:.35rem}.community-shell__metric-value{font-size:2rem;font-weight:700;letter-spacing:-.04em}.community-shell__metric-label{color:var(--community-muted);line-height:1.5}.community-shell :is(rolatech-tabs){--rt-tabs-surface: rgba(148, 163, 184, .12);--rt-tab-selected-background: #0284c7;--rt-tab-hover-background-color: rgba(148, 163, 184, .12);margin-bottom:1.5rem}.community-shell__content{border-radius:32px;padding:clamp(1rem,2vw,1.5rem)}@media(max-width:840px){.community-shell__header,.community-shell__hero{grid-template-columns:1fr;display:grid}.community-shell__theme-button{width:fit-content}.community-shell__hero-card{grid-template-columns:1fr}}\n"] }]
|
|
70
|
+
}], ctorParameters: () => [], propDecorators: { navIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "navIndex", required: false }] }, { type: i0.Output, args: ["navIndexChange"] }] } });
|
|
71
|
+
|
|
72
|
+
const communityAppRoutes = [
|
|
73
|
+
{
|
|
74
|
+
path: '',
|
|
75
|
+
component: CommunityLayoutComponent,
|
|
76
|
+
children: [
|
|
77
|
+
{
|
|
78
|
+
path: '',
|
|
79
|
+
loadComponent: () => import('./rolatech-angular-community-community-index.component-CJ_ddZSo.mjs').then((x) => x.CommunityIndexComponent),
|
|
80
|
+
data: { title: 'Community' },
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
path: 'manage',
|
|
84
|
+
loadComponent: () => import('./rolatech-angular-community-community-manage-index.component-CQYy_iI7.mjs').then((x) => x.CommunityManageIndexComponent),
|
|
85
|
+
data: { title: 'Manage Posts' },
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
path: 'manage/create',
|
|
89
|
+
loadComponent: () => import('./rolatech-angular-community-community-manage-post.component-DVRDQzXA.mjs').then((x) => x.CommunityManagePostComponent),
|
|
90
|
+
data: { title: 'Write Post' },
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
path: 'manage/:id',
|
|
94
|
+
loadComponent: () => import('./rolatech-angular-community-community-manage-post-details.component-B70ewcPc.mjs').then((x) => x.CommunityManagePostDetailsComponent),
|
|
95
|
+
data: { title: 'Post Overview' },
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
path: 'manage/:id/edit',
|
|
99
|
+
loadComponent: () => import('./rolatech-angular-community-community-manage-post.component-DVRDQzXA.mjs').then((x) => x.CommunityManagePostComponent),
|
|
100
|
+
data: { title: 'Edit Post' },
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
path: ':id',
|
|
104
|
+
loadComponent: () => import('./rolatech-angular-community-community-details.component-B4kDYAna.mjs').then((x) => x.CommunityDetailsComponent),
|
|
105
|
+
data: { title: 'Post' },
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
const communityRoutes = [
|
|
112
|
+
{
|
|
113
|
+
path: '',
|
|
114
|
+
component: CommunityLayoutComponent,
|
|
115
|
+
children: [
|
|
116
|
+
{
|
|
117
|
+
path: '',
|
|
118
|
+
loadComponent: () => import('./rolatech-angular-community-community-index.component-CJ_ddZSo.mjs').then((x) => x.CommunityIndexComponent),
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
path: ':id',
|
|
122
|
+
loadComponent: () => import('./rolatech-angular-community-community-details.component-B4kDYAna.mjs').then((x) => x.CommunityDetailsComponent),
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
const communityManageRoutes = [
|
|
129
|
+
{
|
|
130
|
+
path: '',
|
|
131
|
+
loadComponent: () => import('./rolatech-angular-community-community-manage-index.component-CQYy_iI7.mjs').then((x) => x.CommunityManageIndexComponent),
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
path: 'create',
|
|
135
|
+
loadComponent: () => import('./rolatech-angular-community-community-manage-post.component-DVRDQzXA.mjs').then((x) => x.CommunityManagePostComponent),
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
path: ':id',
|
|
139
|
+
loadComponent: () => import('./rolatech-angular-community-community-manage-post-details.component-B70ewcPc.mjs').then((x) => x.CommunityManagePostDetailsComponent),
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
path: ':id/edit',
|
|
143
|
+
loadComponent: () => import('./rolatech-angular-community-community-manage-post.component-DVRDQzXA.mjs').then((x) => x.CommunityManagePostComponent),
|
|
144
|
+
},
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
class CommunityService extends BaseService {
|
|
148
|
+
init() {
|
|
149
|
+
this.endpoint = 'community';
|
|
150
|
+
super.init();
|
|
151
|
+
}
|
|
152
|
+
uploadMedia(id, data) {
|
|
153
|
+
return this.http.post(`${this.actionUrl}/${id}/media`, data, {
|
|
154
|
+
withCredentials: true,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
deleteMedia(id, mediaId) {
|
|
158
|
+
return this.http.delete(`${this.actionUrl}/${id}/media/${mediaId}`, {
|
|
159
|
+
withCredentials: true,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: CommunityService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
163
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: CommunityService, providedIn: 'root' }); }
|
|
164
|
+
}
|
|
165
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: CommunityService, decorators: [{
|
|
166
|
+
type: Injectable,
|
|
167
|
+
args: [{
|
|
168
|
+
providedIn: 'root',
|
|
169
|
+
}]
|
|
170
|
+
}] });
|
|
171
|
+
|
|
172
|
+
const now = new Date('2026-03-11T09:00:00.000Z').toISOString();
|
|
173
|
+
const COMMUNITY_POST_SEED = [
|
|
174
|
+
{
|
|
175
|
+
id: 'seed-angular-signals-at-scale',
|
|
176
|
+
slug: 'angular-signals-at-scale',
|
|
177
|
+
title: 'Angular signals at product scale',
|
|
178
|
+
summary: 'A practical checklist for keeping signal-driven screens predictable as feature count and team size grow.',
|
|
179
|
+
content: `# Angular signals at product scale
|
|
180
|
+
|
|
181
|
+
Shipping signal-heavy Angular apps gets harder once the feature surface expands. The UI stays fast, but the mental model starts to drift unless the team is strict about ownership.
|
|
182
|
+
|
|
183
|
+
## What breaks first
|
|
184
|
+
|
|
185
|
+
- derived state gets duplicated in components and services
|
|
186
|
+
- route params become hidden dependencies
|
|
187
|
+
- optimistic updates and server refreshes stop agreeing
|
|
188
|
+
|
|
189
|
+
## What scales better
|
|
190
|
+
|
|
191
|
+
1. Keep server state boundaries obvious.
|
|
192
|
+
2. Prefer one write path for each user action.
|
|
193
|
+
3. Render markdown, counters, and labels from computed state instead of mutating component fields.
|
|
194
|
+
|
|
195
|
+
## Review rule
|
|
196
|
+
|
|
197
|
+
If a screen needs more than one place to understand why a button is enabled, the state graph is already too wide.`,
|
|
198
|
+
tags: ['angular', 'signals', 'architecture'],
|
|
199
|
+
status: 'PUBLISHED',
|
|
200
|
+
authorName: 'RolaTech Editorial',
|
|
201
|
+
createdAt: now,
|
|
202
|
+
updatedAt: now,
|
|
203
|
+
publishedAt: now,
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
id: 'seed-designing-admin-workflows',
|
|
207
|
+
slug: 'designing-admin-workflows',
|
|
208
|
+
title: 'Designing admin workflows that do not collapse under edge cases',
|
|
209
|
+
summary: 'Treat empty states, retries, and draft saves as first-class flows or the interface will punish operations teams.',
|
|
210
|
+
content: `# Designing admin workflows that do not collapse under edge cases
|
|
211
|
+
|
|
212
|
+
Good admin software is less about the happy path and more about preserving momentum when data is partial, stale, or late.
|
|
213
|
+
|
|
214
|
+
## Baseline expectations
|
|
215
|
+
|
|
216
|
+
- drafts survive refreshes
|
|
217
|
+
- previews reflect the exact payload you are about to send
|
|
218
|
+
- table filters map cleanly to route state
|
|
219
|
+
|
|
220
|
+
## Implementation notes
|
|
221
|
+
|
|
222
|
+
Use route-level shells for navigation and theme state. Keep editor state local to the page. Push only durable data into shared services.`,
|
|
223
|
+
tags: ['design-system', 'operations', 'ux'],
|
|
224
|
+
status: 'PUBLISHED',
|
|
225
|
+
authorName: 'RolaTech Editorial',
|
|
226
|
+
createdAt: now,
|
|
227
|
+
updatedAt: now,
|
|
228
|
+
publishedAt: now,
|
|
229
|
+
},
|
|
230
|
+
];
|
|
231
|
+
|
|
232
|
+
function renderMarkdown(content) {
|
|
233
|
+
return String(marked.parse(content ?? ''));
|
|
234
|
+
}
|
|
235
|
+
function stripMarkdown(content) {
|
|
236
|
+
return (content ?? '')
|
|
237
|
+
.replace(/```[\s\S]*?```/g, ' ')
|
|
238
|
+
.replace(/`([^`]+)`/g, '$1')
|
|
239
|
+
.replace(/!\[([^\]]*)\]\([^)]+\)/g, '$1')
|
|
240
|
+
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
|
|
241
|
+
.replace(/^#+\s+/gm, '')
|
|
242
|
+
.replace(/[*_>~-]/g, ' ')
|
|
243
|
+
.replace(/\s+/g, ' ')
|
|
244
|
+
.trim();
|
|
245
|
+
}
|
|
246
|
+
function countWords(content) {
|
|
247
|
+
const matches = stripMarkdown(content).match(/[A-Za-z0-9'-]+/g);
|
|
248
|
+
return matches?.length ?? 0;
|
|
249
|
+
}
|
|
250
|
+
function estimateReadingTime(content) {
|
|
251
|
+
return Math.max(1, Math.ceil(countWords(content) / 180));
|
|
252
|
+
}
|
|
253
|
+
function createPostPreview(content) {
|
|
254
|
+
const plainText = stripMarkdown(content);
|
|
255
|
+
return {
|
|
256
|
+
html: renderMarkdown(content),
|
|
257
|
+
plainText,
|
|
258
|
+
wordCount: countWords(content),
|
|
259
|
+
readingTimeMinutes: estimateReadingTime(content),
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
function normalizeTags(tags) {
|
|
263
|
+
if (Array.isArray(tags)) {
|
|
264
|
+
return tags.map((tag) => tag.trim()).filter(Boolean);
|
|
265
|
+
}
|
|
266
|
+
if (typeof tags === 'string') {
|
|
267
|
+
return tags
|
|
268
|
+
.split(',')
|
|
269
|
+
.map((tag) => tag.trim())
|
|
270
|
+
.filter(Boolean);
|
|
271
|
+
}
|
|
272
|
+
return [];
|
|
273
|
+
}
|
|
274
|
+
function toTagInput(tags) {
|
|
275
|
+
return normalizeTags(tags).join(', ');
|
|
276
|
+
}
|
|
277
|
+
function createSlug(value) {
|
|
278
|
+
return (value ?? '')
|
|
279
|
+
.toLowerCase()
|
|
280
|
+
.trim()
|
|
281
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
282
|
+
.replace(/^-+|-+$/g, '');
|
|
283
|
+
}
|
|
284
|
+
function resolveSummary(post) {
|
|
285
|
+
if (post.summary?.trim()) {
|
|
286
|
+
return post.summary.trim();
|
|
287
|
+
}
|
|
288
|
+
if (post.excerpt?.trim()) {
|
|
289
|
+
return post.excerpt.trim();
|
|
290
|
+
}
|
|
291
|
+
return stripMarkdown(post.content).slice(0, 180).trim();
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
class CommunityPostStoreService {
|
|
295
|
+
constructor() {
|
|
296
|
+
this.platformId = inject(PLATFORM_ID);
|
|
297
|
+
this.postService = inject(PostService);
|
|
298
|
+
this.isBrowser = isPlatformBrowser(this.platformId);
|
|
299
|
+
this.storageKey = 'rolatech-community-posts';
|
|
300
|
+
}
|
|
301
|
+
listPublished(options) {
|
|
302
|
+
return this.postService.listPublished({ ...options, sort: options?.sort ?? 'publishedAt desc' }).pipe(map((response) => this.mergePublishedPosts(this.normalizeListResponse(response), options)), catchError(() => of(this.mergePublishedPosts([], options))));
|
|
303
|
+
}
|
|
304
|
+
getPublishedPost(idOrSlug) {
|
|
305
|
+
return this.postService.getByIdOrSlug(idOrSlug).pipe(map((response) => this.normalizeSingleResponse(response) ?? this.findFallbackPublishedPost(idOrSlug)), catchError(() => of(this.findFallbackPublishedPost(idOrSlug))));
|
|
306
|
+
}
|
|
307
|
+
listMyPosts(options) {
|
|
308
|
+
return this.postService.me({ ...options, sort: options?.sort ?? 'updatedAt desc' }).pipe(map((response) => this.filterPosts(this.mergePosts(this.normalizeListResponse(response), this.getLocalPosts()), options)), catchError(() => of(this.filterPosts(this.getLocalPosts(), options))));
|
|
309
|
+
}
|
|
310
|
+
getManagePost(idOrSlug) {
|
|
311
|
+
const local = this.findLocalPost(idOrSlug);
|
|
312
|
+
if (local) {
|
|
313
|
+
return of(local);
|
|
314
|
+
}
|
|
315
|
+
return this.postService.getByIdOrSlug(idOrSlug, true).pipe(map((response) => this.normalizeSingleResponse(response)), catchError(() => of(null)));
|
|
316
|
+
}
|
|
317
|
+
saveDraft(payload) {
|
|
318
|
+
const normalizedPayload = this.normalizePayload(payload, 'DRAFT');
|
|
319
|
+
const localFallback = () => of(this.persistLocalPost(this.buildLocalPost(payload, normalizedPayload, 'DRAFT')));
|
|
320
|
+
if (!payload.id) {
|
|
321
|
+
return this.postService.createPost({ ...normalizedPayload, status: 'DRAFT' }).pipe(map((response) => this.persistLocalPost(this.normalizeSingleResponse(response) ?? this.buildLocalPost(payload, normalizedPayload, 'DRAFT'))), catchError(() => localFallback()));
|
|
322
|
+
}
|
|
323
|
+
if (payload.id.startsWith('local-')) {
|
|
324
|
+
return localFallback();
|
|
325
|
+
}
|
|
326
|
+
return this.postService.saveDraft(payload.id, normalizedPayload).pipe(map((response) => this.persistLocalPost(this.normalizeSingleResponse(response) ?? this.buildLocalPost(payload, normalizedPayload, 'DRAFT'))), catchError(() => localFallback()));
|
|
327
|
+
}
|
|
328
|
+
publish(payload) {
|
|
329
|
+
const normalizedPayload = this.normalizePayload(payload, 'PUBLISHED');
|
|
330
|
+
const localFallback = () => of(this.persistLocalPost(this.buildLocalPost(payload, normalizedPayload, 'PUBLISHED')));
|
|
331
|
+
if (!payload.id) {
|
|
332
|
+
return this.postService.createPost({ ...normalizedPayload, status: 'PUBLISHED' }).pipe(map((response) => this.persistLocalPost(this.normalizeSingleResponse(response) ?? this.buildLocalPost(payload, normalizedPayload, 'PUBLISHED'))), catchError(() => localFallback()));
|
|
333
|
+
}
|
|
334
|
+
if (payload.id.startsWith('local-')) {
|
|
335
|
+
return localFallback();
|
|
336
|
+
}
|
|
337
|
+
return this.postService.publish(payload.id, normalizedPayload).pipe(map((response) => this.persistLocalPost(this.normalizeSingleResponse(response) ?? this.buildLocalPost(payload, normalizedPayload, 'PUBLISHED'))), catchError(() => localFallback()));
|
|
338
|
+
}
|
|
339
|
+
mergePublishedPosts(remotePosts, options) {
|
|
340
|
+
const localPublished = this.getLocalPosts().filter((post) => this.toStatus(post.status) === 'PUBLISHED');
|
|
341
|
+
const combined = this.mergePosts(remotePosts, localPublished, remotePosts.length || localPublished.length ? [] : COMMUNITY_POST_SEED);
|
|
342
|
+
return this.filterPosts(combined, { ...options, status: 'PUBLISHED' });
|
|
343
|
+
}
|
|
344
|
+
mergePosts(...groups) {
|
|
345
|
+
const registry = new Map();
|
|
346
|
+
groups.flat().forEach((post) => {
|
|
347
|
+
const normalized = this.normalizePost(post);
|
|
348
|
+
registry.set(normalized.id, normalized);
|
|
349
|
+
});
|
|
350
|
+
return Array.from(registry.values()).sort((a, b) => {
|
|
351
|
+
const aDate = new Date(a.updatedAt ?? a.publishedAt ?? a.createdAt ?? 0).getTime();
|
|
352
|
+
const bDate = new Date(b.updatedAt ?? b.publishedAt ?? b.createdAt ?? 0).getTime();
|
|
353
|
+
return bDate - aDate;
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
normalizeListResponse(response) {
|
|
357
|
+
const candidates = response?.data?.items ?? response?.data ?? response?.items ?? response;
|
|
358
|
+
if (Array.isArray(candidates)) {
|
|
359
|
+
return candidates.map((item) => this.normalizePost(item));
|
|
360
|
+
}
|
|
361
|
+
if (Array.isArray(candidates?.items)) {
|
|
362
|
+
return candidates.items.map((item) => this.normalizePost(item));
|
|
363
|
+
}
|
|
364
|
+
return [];
|
|
365
|
+
}
|
|
366
|
+
normalizeSingleResponse(response) {
|
|
367
|
+
const candidate = response?.data ?? response;
|
|
368
|
+
if (!candidate || Array.isArray(candidate)) {
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
return this.normalizePost(candidate);
|
|
372
|
+
}
|
|
373
|
+
normalizePost(post) {
|
|
374
|
+
const id = String(post?.id ?? post?.slug ?? this.generateLocalId());
|
|
375
|
+
const status = this.toStatus(post?.status);
|
|
376
|
+
const createdAt = post?.createdAt ?? post?.publishedAt ?? new Date().toISOString();
|
|
377
|
+
const updatedAt = post?.updatedAt ?? createdAt;
|
|
378
|
+
const publishedAt = status === 'PUBLISHED' ? post?.publishedAt ?? updatedAt : null;
|
|
379
|
+
return {
|
|
380
|
+
id,
|
|
381
|
+
title: post?.title ?? 'Untitled post',
|
|
382
|
+
content: post?.content ?? post?.body ?? '',
|
|
383
|
+
summary: post?.summary ?? null,
|
|
384
|
+
excerpt: post?.excerpt ?? null,
|
|
385
|
+
slug: post?.slug ?? createSlug(post?.title ?? id),
|
|
386
|
+
coverImage: post?.coverImage ?? post?.cover ?? null,
|
|
387
|
+
cover: post?.cover ?? post?.coverImage ?? null,
|
|
388
|
+
tags: normalizeTags(post?.tags),
|
|
389
|
+
status,
|
|
390
|
+
author: post?.author
|
|
391
|
+
? {
|
|
392
|
+
id: post.author.id ?? null,
|
|
393
|
+
name: post.author.name ?? null,
|
|
394
|
+
username: post.author.username ?? null,
|
|
395
|
+
avatar: post.author.avatar ?? null,
|
|
396
|
+
}
|
|
397
|
+
: null,
|
|
398
|
+
authorName: post?.authorName ?? post?.author?.name ?? 'RolaTech',
|
|
399
|
+
createdAt,
|
|
400
|
+
updatedAt,
|
|
401
|
+
publishedAt,
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
normalizePayload(payload, status) {
|
|
405
|
+
return {
|
|
406
|
+
title: (payload.title ?? 'Untitled post').trim(),
|
|
407
|
+
content: payload.content ?? '',
|
|
408
|
+
summary: resolveSummary(payload),
|
|
409
|
+
excerpt: payload.excerpt ?? resolveSummary(payload),
|
|
410
|
+
slug: createSlug(payload.slug || payload.title || 'untitled-post'),
|
|
411
|
+
coverImage: payload.coverImage ?? payload.cover ?? null,
|
|
412
|
+
cover: payload.cover ?? payload.coverImage ?? null,
|
|
413
|
+
tags: normalizeTags(payload.tags),
|
|
414
|
+
status,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
buildLocalPost(source, payload, status) {
|
|
418
|
+
const existing = source.id ? this.findLocalPost(source.id) : null;
|
|
419
|
+
const now = new Date().toISOString();
|
|
420
|
+
return {
|
|
421
|
+
id: source.id ?? existing?.id ?? this.generateLocalId(),
|
|
422
|
+
title: payload.title,
|
|
423
|
+
content: payload.content,
|
|
424
|
+
summary: payload.summary ?? null,
|
|
425
|
+
excerpt: payload.excerpt ?? null,
|
|
426
|
+
slug: payload.slug ?? createSlug(payload.title),
|
|
427
|
+
coverImage: payload.coverImage ?? null,
|
|
428
|
+
cover: payload.cover ?? null,
|
|
429
|
+
tags: payload.tags ?? [],
|
|
430
|
+
status,
|
|
431
|
+
authorName: existing?.authorName ?? 'You',
|
|
432
|
+
createdAt: existing?.createdAt ?? now,
|
|
433
|
+
updatedAt: now,
|
|
434
|
+
publishedAt: status === 'PUBLISHED' ? now : existing?.publishedAt ?? null,
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
filterPosts(posts, options) {
|
|
438
|
+
const search = options?.search?.trim().toLowerCase();
|
|
439
|
+
const status = options?.status?.trim().toUpperCase();
|
|
440
|
+
return posts.filter((post) => {
|
|
441
|
+
const matchesStatus = !status || this.toStatus(post.status) === status;
|
|
442
|
+
const haystack = [post.title, post.summary, post.authorName, ...(post.tags ?? [])].join(' ').toLowerCase();
|
|
443
|
+
const matchesSearch = !search || haystack.includes(search);
|
|
444
|
+
return matchesStatus && matchesSearch;
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
findFallbackPublishedPost(idOrSlug) {
|
|
448
|
+
return this.mergePublishedPosts([]).find((post) => post.id === idOrSlug || post.slug === idOrSlug) ?? null;
|
|
449
|
+
}
|
|
450
|
+
findLocalPost(idOrSlug) {
|
|
451
|
+
return this.getLocalPosts().find((post) => post.id === idOrSlug || post.slug === idOrSlug) ?? null;
|
|
452
|
+
}
|
|
453
|
+
persistLocalPost(post) {
|
|
454
|
+
if (!this.isBrowser) {
|
|
455
|
+
return post;
|
|
456
|
+
}
|
|
457
|
+
const posts = this.getLocalPosts();
|
|
458
|
+
const next = posts.filter((item) => item.id !== post.id);
|
|
459
|
+
next.unshift(post);
|
|
460
|
+
localStorage.setItem(this.storageKey, JSON.stringify(next));
|
|
461
|
+
return post;
|
|
462
|
+
}
|
|
463
|
+
getLocalPosts() {
|
|
464
|
+
if (!this.isBrowser) {
|
|
465
|
+
return [];
|
|
466
|
+
}
|
|
467
|
+
const raw = localStorage.getItem(this.storageKey);
|
|
468
|
+
if (!raw) {
|
|
469
|
+
return [];
|
|
470
|
+
}
|
|
471
|
+
try {
|
|
472
|
+
const parsed = JSON.parse(raw);
|
|
473
|
+
return Array.isArray(parsed) ? parsed.map((item) => this.normalizePost(item)) : [];
|
|
474
|
+
}
|
|
475
|
+
catch {
|
|
476
|
+
return [];
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
generateLocalId() {
|
|
480
|
+
return `local-${Math.random().toString(36).slice(2, 10)}`;
|
|
481
|
+
}
|
|
482
|
+
toStatus(status) {
|
|
483
|
+
return (status ?? 'DRAFT').toString().trim().toUpperCase();
|
|
484
|
+
}
|
|
485
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: CommunityPostStoreService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
486
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: CommunityPostStoreService, providedIn: 'root' }); }
|
|
487
|
+
}
|
|
488
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: CommunityPostStoreService, decorators: [{
|
|
489
|
+
type: Injectable,
|
|
490
|
+
args: [{
|
|
491
|
+
providedIn: 'root',
|
|
492
|
+
}]
|
|
493
|
+
}] });
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Generated bundle index. Do not edit.
|
|
497
|
+
*/
|
|
498
|
+
|
|
499
|
+
export { CommunityPostStoreService as C, communityAppRoutes as a, communityRoutes as b, createPostPreview as c, communityManageRoutes as d, CommunityService as e, toTagInput as t };
|
|
500
|
+
//# sourceMappingURL=rolatech-angular-community-rolatech-angular-community-40yr8v0S.mjs.map
|