Skip to content

Commit 1185ddc

Browse files
Enhance post creation and update functionality to include tag management
1 parent 65fc430 commit 1185ddc

File tree

4 files changed

+252
-20
lines changed

4 files changed

+252
-20
lines changed

llms.txt

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Angular
2+
3+
Angular — Deliver web apps with confidence 🚀
4+
5+
## Table of Contents
6+
7+
- [What is Angular](https://angular.dev/overview)
8+
- [Installation guide](https://angular.dev/installation)
9+
- [Style Guide](https://next.angular.dev/style-guide)
10+
11+
## Components
12+
13+
- [What is a component](https://angular.dev/guide/components)
14+
- [Component selectors](https://angular.dev/guide/components/selectors)
15+
- [Styling components](https://angular.dev/guide/components/styling)
16+
- [Accepting data with input properties](https://angular.dev/guide/components/inputs)
17+
- [Custom events with output](https://angular.dev/guide/components/outputs)
18+
- [Content projection](https://angular.dev/guide/components/content-projection)
19+
- [Component lifecycle](https://angular.dev/guide/components/lifecycle)
20+
21+
## Templates guides
22+
23+
- [Template Overview](https://angular.dev/guide/templates)
24+
- [Adding event listeners](https://angular.dev/guide/templates/event-listeners)
25+
- [Binding text, properties and attributes](https://angular.dev/guide/templates/binding)
26+
- [Control Flow](https://angular.dev/guide/templates/control-flow)
27+
- [Template variable declaration](https://angular.dev/guide/templates/variables)
28+
- [Deferred loading of components](https://angular.dev/guide/templates/defer)
29+
- [Expression syntax](https://angular.dev/guide/templates/expression-syntax)
30+
31+
## Directives
32+
33+
- [Directives overview](https://angular.dev/guide/directives)
34+
- [Attribute directives](https://angular.dev/guide/directives/attribute-directives)
35+
- [Structural directives](https://angular.dev/guide/directives/structural-directives)
36+
- [Directive composition](https://angular.dev/guide/directives/directive-composition-api)
37+
- [Optimizing images](https://angular.dev/guide/image-optimization)
38+
39+
## Signals
40+
41+
- [Signals overview](https://angular.dev/guide/signals)
42+
- [Dependent state with linkedSignal](https://angular.dev/guide/signals/linked-signal)
43+
- [Async reactivity with resources](https://angular.dev/guide/signals/resource)
44+
45+
## Dependency injection (DI)
46+
47+
- [Dependency Injection overview](https://angular.dev/guide/di)
48+
- [Understanding Dependency injection](https://angular.dev/guide/di/dependency-injection)
49+
- [Creating an injectable service](https://angular.dev/guide/di/creating-injectable-service)
50+
- [Configuring dependency providers](https://angular.dev/guide/di/dependency-injection-providers)
51+
- [Injection context](https://angular.dev/guide/di/dependency-injection-context)
52+
- [Hierarchical injectors](https://angular.dev/guide/di/hierarchical-dependency-injection)
53+
- [Optimizing Injection tokens](https://angular.dev/guide/di/lightweight-injection-tokens)
54+
55+
## RxJS
56+
57+
- [RxJS interop with Angular signals](https://angular.dev/ecosystem/rxjs-interop)
58+
- [Component output interop](https://angular.dev/ecosystem/rxjs-interop/output-interop)
59+
60+
## Loading Data
61+
62+
- [HttpClient overview](https://angular.dev/guide/http)
63+
- [Setting up the HttpClient](https://angular.dev/guide/http/setup)
64+
- [Making requests](https://angular.dev/guide/http/making-requests)
65+
- [Intercepting requests](https://angular.dev/guide/http/interceptors)
66+
- [Testing](https://angular.dev/guide/http/testing)
67+
68+
## Forms
69+
- [Forms overview](https://angular.dev/guide/forms)
70+
- [Reactive Forms](https://angular.dev/guide/forms/reactive-forms)
71+
- [Strictly types forms](https://angular.dev/guide/forms/typed-forms)
72+
- [Template driven forms](https://angular.dev/guide/forms/template-driven-forms)
73+
- [Validate forms input](https://angular.dev/guide/forms/form-validation)
74+
- [Building dynamic forms](https://angular.dev/guide/forms/dynamic-forms)
75+
76+
## Routing
77+
- [Routing overview](https://angular.dev/guide/routing)
78+
- [Common routing tasks](https://angular.dev/guide/routing/common-router-tasks)
79+
- [Routing in an SPA](https://angular.dev/guide/routing/router-tutorial)
80+
- [Creating custom route matches](https://angular.dev/guide/routing/routing-with-urlmatcher)
81+
82+
## Server Side Rendering (SSR)
83+
84+
- [SSR Overview](https://angular.dev/guide/performance)
85+
- [SSR with Angular](https://angular.dev/guide/ssr)
86+
- [Build-time prerendering (SSG)](https://angular.dev/guide/prerendering)
87+
- [Hybrid rendering with server routing](https://angular.dev/guide/hybrid-rendering)
88+
- [Hydration](https://angular.dev/guide/hydration)
89+
- [Incremental Hydration](https://angular.dev/guide/incremental-hydration)
90+
91+
# CLI
92+
[Angular CLI Overview](https://angular.dev/tools/cli)
93+
94+
## Testing
95+
96+
- [Testing overview](https://angular.dev/guide/testing)
97+
- [Testing coverage](https://angular.dev/guide/testing/code-coverage)
98+
- [Testing services](https://angular.dev/guide/testing/services)
99+
- [Basics of component testing](https://angular.dev/guide/testing/components-basics)
100+
- [Component testing scenarios](https://angular.dev/guide/testing/components-scenarios)
101+
- [Testing attribute directives](https://angular.dev/guide/testing/attribute-directives
102+
- [Testing pipes](https://angular.dev/guide/testing/pipes
103+
- [Debugging tests](https://angular.dev/guide/testing/debugging)
104+
- [Testing utility apis](https://angular.dev/guide/testing/utility-apis)
105+
- [Component harness overview](https://angular.dev/guide/testing/component-harnesses-overview)
106+
- [Using component harness in tests](https://angular.dev/guide/testing/using-component-harnesses)
107+
- [Creating a component harness for your components](https://angular.dev/guide/testing/creating-component-harnesses)
108+
109+
## Animations
110+
- [Animations your content](https://angular.dev/guide/animations/css)
111+
- [Route transition animation](https://angular.dev/guide/animations/route-animations)
112+
- [Migrating to native CSS animations](https://next.angular.dev/guide/animations/migration)
113+
114+
## APIs
115+
- [API reference](https://angular.dev/api)
116+
- [CLI command reference](https://angular.dev/cli)
117+
118+
119+
## Others
120+
121+
- [Zoneless](https://angular.dev/guide/experimental/zoneless)
122+
- [Error encyclopedia](https://angular.dev/errors)
123+
- [Extended diagnostics](https://angular.dev/extended-diagnostics)
124+
- [Update guide](https://angular.dev/update-guide)
125+
- [Contribute to Angular](https://github.com/angular/angular/blob/main/CONTRIBUTING.md)
126+
- [Angular's Roadmap](https://angular.dev/roadmap)
127+
- [Keeping your projects up-to-date](https://angular.dev/update)
128+
- [Security](https://angular.dev/best-practices/security)
129+
- [Internationalization (i18n)](https://angular.dev/guide/i18n)

src/app/admin/_components/add-post/add-post.component.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ export class AddPostComponent implements OnInit {
9595

9696
onSubmit(isDraft = false): void {
9797
this.highlightContent();
98-
//test for description
98+
// Test for description
9999
if (!this.blogForm?.controls?.description?.value) {
100100
this.blogForm.controls?.description?.setValue(
101101
this.blogForm.controls.content.value.toString().substring(0, 150),
@@ -107,16 +107,21 @@ export class AddPostComponent implements OnInit {
107107
const cleanedContent = rawContent.replace(/( |\u00A0)/g, ' ');
108108
this.blogForm.controls.content.setValue(cleanedContent);
109109
this.blogForm.controls.is_draft.setValue(isDraft);
110+
110111
if (!this.blogForm.controls.created_at.value) {
111112
this.blogForm.controls.created_at.setValue(null);
112113
}
114+
115+
// Extract form data including tags
116+
const formData = {
117+
...this.blogForm.value,
118+
tags: this.blogForm.controls.tags.value,
119+
};
120+
113121
if (this.postId) {
114-
this.apiService.updatePost(
115-
this.postId,
116-
this.blogForm.value as PostUpdate,
117-
);
122+
this.apiService.updatePost(this.postId, formData as PostUpdate & { tags: Tag[] });
118123
} else {
119-
this.apiService.addPost(this.blogForm.value as PostInsert);
124+
this.apiService.addPost(formData as PostInsert & { tags: Tag[] });
120125
}
121126
}
122127
}

src/app/admin/_services/admin-api.service.ts

Lines changed: 111 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { inject, Injectable } from '@angular/core';
22
import { map, Observable } from 'rxjs';
3-
import { Post, PostInsert, PostUpdate } from '../../supabase-types';
3+
import { Post, PostInsert, PostUpdate, Tag } from '../../supabase-types';
44
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
55
import { SupabaseService } from '../../services/supabase.service';
66
import { environment } from '../../../environments/environment.local';
@@ -12,10 +12,42 @@ export class AdminApiService {
1212
private readonly baseUrl = `${environment.supabaseUrl}/rest/v1/`;
1313
private readonly apiKey = environment.supabaseKey;
1414

15-
async addPost(post: PostInsert): Promise<void> {
16-
const { error } = await this.supabaseService.getClient
17-
.from('posts')
18-
.insert({ ...post });
15+
async addPost(post: PostInsert & { tags?: Tag[] }): Promise<void> {
16+
const { tags, ...postData } = post;
17+
18+
try {
19+
const { data: insertedPost, error: postError } = await this.supabaseService.getClient
20+
.from('posts')
21+
.insert({ ...postData })
22+
.select('id')
23+
.single();
24+
25+
if (postError) {
26+
console.error('Error inserting post:', postError);
27+
throw postError;
28+
}
29+
30+
if (tags && tags.length > 0 && insertedPost) {
31+
const postTagInserts = tags.map(tag => ({
32+
post_id: insertedPost.id,
33+
tag_id: tag.id
34+
}));
35+
36+
const { error: tagError } = await this.supabaseService.getClient
37+
.from('post_tags')
38+
.insert(postTagInserts);
39+
40+
if (tagError) {
41+
console.error('Error inserting post tags:', tagError);
42+
throw tagError;
43+
}
44+
}
45+
46+
console.log('Post created successfully with tags');
47+
} catch (error) {
48+
console.error('Failed to create post:', error);
49+
throw error;
50+
}
1951
}
2052

2153
getPostById(id: string): Observable<Post> {
@@ -43,13 +75,79 @@ export class AdminApiService {
4375
.pipe(map((results) => results[0] ?? null));
4476
}
4577

46-
async updatePost(id: string, post: PostUpdate): Promise<void> {
47-
await this.supabaseService.getClient
48-
.from('posts')
49-
.update({ ...post })
50-
.eq('id', id)
51-
.then((x) => {
52-
console.log(x);
53-
});
78+
async updatePost(id: string, post: PostUpdate & { tags?: Tag[] }): Promise<void> {
79+
const { tags, ...postData } = post;
80+
81+
try {
82+
// Update the post
83+
const { error: postError } = await this.supabaseService.getClient
84+
.from('posts')
85+
.update({ ...postData })
86+
.eq('id', id);
87+
88+
if (postError) {
89+
console.error('Error updating post:', postError);
90+
throw postError;
91+
}
92+
93+
// Handle tags if provided
94+
if (tags !== undefined) {
95+
// Get existing tags for comparison
96+
const { data: existingPostTags, error: fetchError } = await this.supabaseService.getClient
97+
.from('post_tags')
98+
.select('tag_id')
99+
.eq('post_id', id);
100+
101+
if (fetchError) {
102+
console.error('Error fetching existing post tags:', fetchError);
103+
throw fetchError;
104+
}
105+
106+
const existingTagIds = (existingPostTags || []).map(pt => pt.tag_id).sort();
107+
const newTagIds = tags.map(tag => tag.id).sort();
108+
109+
// Check if tags have actually changed using JSON comparison for better accuracy
110+
const tagsChanged = JSON.stringify(existingTagIds) !== JSON.stringify(newTagIds);
111+
112+
if (tagsChanged) {
113+
console.log('Tags changed, updating...');
114+
115+
// Delete existing post-tag relationships
116+
const { error: deleteError } = await this.supabaseService.getClient
117+
.from('post_tags')
118+
.delete()
119+
.eq('post_id', id);
120+
121+
if (deleteError) {
122+
console.error('Error deleting existing post tags:', deleteError);
123+
throw deleteError;
124+
}
125+
126+
// Insert new post-tag relationships if tags exist
127+
if (tags.length > 0) {
128+
const postTagInserts = tags.map(tag => ({
129+
post_id: id,
130+
tag_id: tag.id
131+
}));
132+
133+
const { error: insertError } = await this.supabaseService.getClient
134+
.from('post_tags')
135+
.insert(postTagInserts);
136+
137+
if (insertError) {
138+
console.error('Error inserting new post tags:', insertError);
139+
throw insertError;
140+
}
141+
}
142+
} else {
143+
console.log('Tags unchanged, skipping tag update');
144+
}
145+
}
146+
147+
console.log('Post updated successfully');
148+
} catch (error) {
149+
console.error('Failed to update post:', error);
150+
throw error;
151+
}
54152
}
55153
}

supabase/migrations/20250525091356_remote_schema.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ CREATE POLICY "Only Admin can delete comments" ON "public"."comments" FOR DELETE
226226

227227

228228

229-
CREATE POLICY "Only Admin can delete post_tags" ON "public"."post_tags" FOR DELETE USING (("auth"."role"() = 'Admin'::"text"));
229+
CREATE POLICY "Only Admin can delete post_tags" ON "public"."post_tags" FOR DELETE USING (((("auth"."jwt"() -> 'app_metadata'::"text") ->> 'role'::"text") = 'Admin'::"text"));
230230

231231

232232

0 commit comments

Comments
 (0)