Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/ Add series to link related articles #1141

Closed
wants to merge 3 commits into from

Conversation

RangerCreaky
Copy link
Contributor

✨ Codu Pull Request 💻

Fixes #1081

Pull Request details

  • Added an extra input field in the more options section (While creating a new post) under the Canonical Url and Draft link
  • Added a new table in the database called "series"
  • Added a new field in the post table called seriesId referencing the id of the series table

Cases considered

  • CREATE
    When the user creates a new record will be created in the series table

  • EDIT

  1. If the user adds a series name to a post which didn't have a series then a new record will be created
  2. If the user changes the series name to a new series name (which is not present in the db), then a new record will be created in the series table. (The previous series will be removed from the series table, if no other post has that series name to maintain consistency).
  3. If the user removes the series name then the seriesId in the post table will be set to null. (The previous series will be removed from the series table, if no other post has that series name to maintain consistency)
  • DELETE
    If the user deletes the post which had a series name then the series will be removed from the series table, if no other post has that series.

Any Breaking changes

  • None

Note to Reviewers

Please let me know,

  • If there is any case missed
  • any change in coding style required
  • any error case not handled
  • any other change required

Associated Screenshots

UI
image

Working Video
series codu

@RangerCreaky RangerCreaky requested a review from a team as a code owner October 17, 2024 16:35
Copy link

vercel bot commented Oct 17, 2024

@RangerCreaky is attempting to deploy a commit to the Codú Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Contributor

coderabbitai bot commented Oct 17, 2024

Walkthrough

The changes in this pull request introduce a new "Series" feature that allows users to link related articles together. This includes modifications to the Create component, where a new input field for seriesName is added. The database schema is updated to create a "Series" table and modify the "Post" table to include a seriesId. Additionally, new API routes are established for managing series, including a mutation for updating series information. Error handling and user feedback mechanisms are also enhanced to support these changes.

Changes

File Change Summary
app/(app)/create/[[...paramsArr]]/_client.tsx Added seriesUpdate mutation, updated getFormData to include seriesName, modified savePost to execute seriesUpdate in parallel, and added a new input field for seriesName.
drizzle/0011_add_series_update_post.sql Created a "Series" table and modified the "Post" table to include a seriesId column.
schema/post.ts Added optional seriesName field to SavePostSchema and ConfirmPostSchema.
schema/series.ts Introduced UpdateSeriesSchema for validating series updates.
server/api/router/index.ts Added seriesRouter to the appRouter configuration.
server/api/router/post.ts Updated postRouter with series handling logic in delete mutation and editDraft query.
server/api/router/series.ts Introduced seriesRouter with an update mutation for managing series.
server/db/schema.ts Defined a new series table and modified the post table to include seriesId.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CreateComponent
    participant Server
    participant Database

    User->>CreateComponent: Fill out form
    CreateComponent->>Server: Save post with seriesName
    Server->>Database: Update post and series
    Database-->>Server: Confirm update
    Server-->>CreateComponent: Return success
    CreateComponent-->>User: Show success message
Loading

Assessment against linked issues

Objective Addressed Explanation
Allow users to group related articles into a series.
Add a series field to the article creation form and database.
Automatic Navigation for articles in a series. Navigation links are not implemented in this PR.
Show indication of the article’s place within the series. This feature is not included in the current changes.
Articles index in the series should be chronological. Implementation details for chronological indexing are unclear.

Possibly related PRs

  • [fix] Post Sorting and Timestamp Display in Your Posts Section #1095: This PR modifies the postRouter in server/api/router/post.ts, which is directly related to the changes in the main PR that also involves post management and updates to the postRouter. The enhancements in sorting and timestamp handling complement the new series-related functionalities introduced in the main PR.

Suggested labels

hacktoberfest-accepted, hacktoberfest

Suggested reviewers

  • NiallJoeMaher

Poem

🐰 In fields of code, we hop and play,
With series now, we pave the way.
Articles linked, like stars they gleam,
A journey through words, a writer's dream.
So gather your tales, let stories unfold,
In the world of series, let magic behold! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

🧹 Outside diff range and nitpick comments (6)
schema/series.ts (1)

3-6: LGTM: UpdateSeriesSchema is well-defined, with minor suggestions for improvement.

The UpdateSeriesSchema is correctly defined and aligns with the PR objectives. It allows for updating series information with a required postId and an optional seriesName. The use of trim() on seriesName is a good practice.

Consider the following suggestions for improvement:

  1. Add a minimum length validation for seriesName to prevent empty strings after trimming.
  2. Include a maximum length validation for seriesName to prevent excessively long series names.
  3. If applicable, add a more specific validation for postId (e.g., UUID format).

Example implementation:

export const UpdateSeriesSchema = z.object({
    postId: z.string().uuid(), // If postId is expected to be a UUID
    seriesName: z.string().trim().min(1).max(100).optional() // Adjust max length as needed
});

These changes would further enhance the robustness of the schema.

drizzle/0011_add_series_update_post.sql (1)

1-9: LGTM! Consider adding a trigger for "updatedAt".

The "Series" table creation looks good and aligns with the feature requirements. The structure and constraints are well-defined. However, consider adding a trigger to automatically update the "updatedAt" column when a row is modified. This ensures that the "updatedAt" timestamp is always current without relying on application-level logic.

Here's an example of how you could add a trigger:

CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
   NEW."updatedAt" = NOW();
   RETURN NEW;
END;
$$ language 'plpgsql';

CREATE TRIGGER update_series_updated_at
BEFORE UPDATE ON "Series"
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();

This trigger will automatically update the "updatedAt" column whenever a row in the "Series" table is modified.

server/api/router/index.ts (1)

Missing CRUD operations and error handling in seriesRouter.

The seriesRouter currently only implements the create operation. The following essential CRUD operations and error handling are missing:

  1. Read/Get Operation:

    • Implement functions to retrieve series data.
  2. Update Operation:

    • Implement functions to modify existing series data.
  3. Delete Operation:

    • Implement functions to remove series data.
  4. Error Handling:

    • Add error handling mechanisms to manage potential exceptions and ensure application stability.

Please address these missing implementations to ensure seriesRouter is fully functional and robust.

🔗 Analysis chain

Line range hint 11-21: Verify seriesRouter implementation and consider refactoring for better organization.

While the changes look good, I recommend the following:

  1. Verify that the seriesRouter implementation covers all required functionality as per the PR objectives, including creating, editing, and deleting series.

  2. Consider refactoring the router imports and declarations for better organization. You could group related routers together, which might improve readability as the number of routers grows.

Here's a suggested refactor for better organization:

 import { createTRPCRouter } from "../trpc";
 import { postRouter } from "./post";
+import { seriesRouter } from "./series";
 import { profileRouter } from "./profile";
 import { commentRouter } from "./comment";
 import { notificationRouter } from "./notification";
 import { adminRouter } from "./admin";
 import { reportRouter } from "./report";
 import { tagRouter } from "./tag";
-import { seriesRouter } from "./series";

 export const appRouter = createTRPCRouter({
   post: postRouter,
+  series: seriesRouter,
   profile: profileRouter,
   comment: commentRouter,
   notification: notificationRouter,
   admin: adminRouter,
   report: reportRouter,
   tag: tagRouter,
-  series: seriesRouter
 });

This groups the seriesRouter with the postRouter, as they are closely related features.

To verify the seriesRouter implementation:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify seriesRouter implementation

# Test: Check if seriesRouter file exists
if [ ! -f "./server/api/router/series.ts" ]; then
    echo "Error: seriesRouter file not found"
    exit 1
fi

# Test: Check for required CRUD operations in seriesRouter
rg --type typescript "create.*series" ./server/api/router/series.ts
rg --type typescript "get.*series" ./server/api/router/series.ts
rg --type typescript "update.*series" ./server/api/router/series.ts
rg --type typescript "delete.*series" ./server/api/router/series.ts

# Test: Check for error handling in seriesRouter
rg --type typescript "throw.*error" ./server/api/router/series.ts

echo "Manual review required to ensure all operations are correctly implemented."

Length of output: 725


Script:

#!/bin/bash
# Description: Verify seriesRouter implementation

# Test: Check if seriesRouter file exists
if [ ! -f "./server/api/router/series.ts" ]; then
    echo "Error: seriesRouter file not found"
    exit 1
fi

# Test: Check for required CRUD operations in seriesRouter
rg "create.*series" ./server/api/router/series.ts
rg "get.*series" ./server/api/router/series.ts
rg "update.*series" ./server/api/router/series.ts
rg "delete.*series" ./server/api/router/series.ts

# Test: Check for error handling in seriesRouter
rg "throw.*error" ./server/api/router/series.ts

echo "Manual review required to ensure all operations are correctly implemented."

Length of output: 1253


Script:

#!/bin/bash
# Description: Verify seriesRouter implementation with flexible patterns

# Test: Check if seriesRouter file exists
if [ ! -f "./server/api/router/series.ts" ]; then
    echo "Error: seriesRouter file not found"
    exit 1
fi

# Define flexible patterns for CRUD operations
CREATE_PATTERN="create[_a-zA-Z]*series"
GET_PATTERN="get[_a-zA-Z]*series|read[_a-zA-Z]*series|fetch[_a-zA-Z]*series"
UPDATE_PATTERN="update[_a-zA-Z]*series|modify[_a-zA-Z]*series|edit[_a-zA-Z]*series"
DELETE_PATTERN="delete[_a-zA-Z]*series|remove[_a-zA-Z]*series"

# Test: Check for Create operation in seriesRouter
if ! rg -i "$CREATE_PATTERN" ./server/api/router/series.ts > /dev/null; then
    echo "Error: Create operation not found in seriesRouter."
fi

# Test: Check for Read/Get operation in seriesRouter
if ! rg -i "$GET_PATTERN" ./server/api/router/series.ts > /dev/null; then
    echo "Error: Read/Get operation not found in seriesRouter."
fi

# Test: Check for Update operation in seriesRouter
if ! rg -i "$UPDATE_PATTERN" ./server/api/router/series.ts > /dev/null; then
    echo "Error: Update operation not found in seriesRouter."
fi

# Test: Check for Delete operation in seriesRouter
if ! rg -i "$DELETE_PATTERN" ./server/api/router/series.ts > /dev/null; then
    echo "Error: Delete operation not found in seriesRouter."
fi

# Test: Check for error handling in seriesRouter
if ! rg -i "throw\s+new\s+Error" ./server/api/router/series.ts > /dev/null; then
    echo "Error: Error handling not implemented in seriesRouter."
fi

echo "Manual review required to ensure all CRUD operations and error handling are correctly implemented."

Length of output: 1289

schema/post.ts (3)

28-28: LGTM! Consider adding a maximum length constraint.

The addition of the seriesName field to the SavePostSchema is well-implemented. It's correctly defined as an optional trimmed string, which aligns with the PR objectives for the Series feature.

Consider adding a maximum length constraint to the seriesName field to prevent excessively long series names. For example:

seriesName: z.string().trim().max(100, "Max series name length is 100 characters.").optional()

This would ensure consistency with other string fields in the schema that have length constraints.


54-54: LGTM! Consider adding a maximum length constraint for consistency.

The addition of the seriesName field to the ConfirmPostSchema is correct and consistent with the change made to SavePostSchema.

For consistency with the suggestion made for SavePostSchema, consider adding a maximum length constraint to this seriesName field as well:

seriesName: z.string().trim().max(100, "Max series name length is 100 characters.").optional()

This would ensure uniformity across both schemas and prevent excessively long series names.


Action Required: Incomplete handling of seriesName in schema usage locations

The seriesName field is not handled in the following files, which may lead to integration issues:

  • server/api/router/post.ts
  • hooks/useCreatePage.ts

Please update these files to properly handle the new seriesName field in accordance with the schema changes.

🔗 Analysis chain

Line range hint 1-54: Verify schema usage in related components and API routes.

The changes to SavePostSchema and ConfirmPostSchema are minimal, focused, and consistent, which is excellent for maintainability. They align well with the PR objectives for implementing the Series feature.

To ensure smooth integration, please verify that all components and API routes using these schemas have been updated to handle the new seriesName field. Run the following script to identify potential usage locations:

Review the results to ensure that the new seriesName field is properly handled in all relevant locations.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find potential usage of SavePostSchema and ConfirmPostSchema

echo "Searching for SavePostSchema usage:"
rg --type typescript -A 5 "SavePostSchema"

echo "\nSearching for ConfirmPostSchema usage:"
rg --type typescript -A 5 "ConfirmPostSchema"

Length of output: 330


Script:

#!/bin/bash
# Description: Find potential usage of SavePostSchema and ConfirmPostSchema in TypeScript files

echo "Searching for SavePostSchema usage:"
rg -g "*.ts" -g "*.tsx" -A 5 "SavePostSchema"

echo "\nSearching for ConfirmPostSchema usage:"
rg -g "*.ts" -g "*.tsx" -A 5 "ConfirmPostSchema"

Length of output: 3841


Script:

#!/bin/bash
# Description: Verify usage of 'seriesName' in files utilizing SavePostSchema and ConfirmPostSchema

echo "Searching for 'seriesName' usage in files using SavePostSchema and ConfirmPostSchema:"

rg -g "*.ts" -g "*.tsx" "seriesName" server/api/router/post.ts hooks/useCreatePage.ts app/(app)/create/[[...paramsArr]]/_client.tsx

Length of output: 455


Script:

#!/bin/bash
# Description: Verify usage of 'seriesName' in files utilizing SavePostSchema and ConfirmPostSchema

echo "Searching for 'seriesName' usage in files using SavePostSchema and ConfirmPostSchema:"

rg -g "*.ts" -g "*.tsx" "seriesName" "server/api/router/post.ts" "hooks/useCreatePage.ts" "app/(app)/create/[[...paramsArr]]/_client.tsx"

Length of output: 828

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between a30156b and f0161e6.

📒 Files selected for processing (8)
  • app/(app)/create/[[...paramsArr]]/_client.tsx (4 hunks)
  • drizzle/0011_add_series_update_post.sql (1 hunks)
  • schema/post.ts (2 hunks)
  • schema/series.ts (1 hunks)
  • server/api/router/index.ts (2 hunks)
  • server/api/router/post.ts (3 hunks)
  • server/api/router/series.ts (1 hunks)
  • server/db/schema.ts (3 hunks)
🧰 Additional context used
🔇 Additional comments (8)
schema/series.ts (2)

1-1: LGTM: Zod import is correct.

The import statement correctly imports the Zod library, which is necessary for schema validation. Using 'z' as an alias is a common and accepted practice when working with Zod.


1-6: Overall, the schema definition is well-implemented and supports the new Series feature.

The UpdateSeriesSchema provides a solid foundation for validating series update operations. It correctly handles the required postId and optional seriesName, aligning well with the PR objectives. The use of Zod for schema validation is a good choice, providing type safety and runtime checks.

While the current implementation is functional, consider the suggested improvements to make the schema more robust and prevent potential edge cases. These enhancements would further improve data integrity and user experience.

Great job on implementing this new feature!

server/api/router/index.ts (2)

11-11: LGTM: Import statement for seriesRouter is correctly added.

The import statement for seriesRouter is consistent with the existing import style and is appropriately placed at the end of the import block.


21-21: LGTM: seriesRouter correctly added to appRouter.

The seriesRouter is appropriately added to the appRouter, following the established pattern for router additions. This change aligns with the PR objectives of implementing the "Series" feature.

server/api/router/series.ts (1)

1-5: Imports are correctly organized

The necessary modules and functions are properly imported, ensuring that all dependencies are available.

server/api/router/post.ts (2)

17-17: Importing 'series' from the database schema is appropriate

The addition of series to the import statement ensures that the series table can be accessed and manipulated within this file, which is necessary for the new series-related functionality.


444-444: Including 'series' relationship in the editDraft query

Adding series: true to the with clause ensures that the series information is fetched alongside the post data. This is necessary for editing posts that are part of a series, allowing the front-end to display and manage the series information appropriately.

server/db/schema.ts (1)

190-190: Ensure consistency in relation definitions

The addition of the series relation in postRelations looks good. Just ensure that the foreign key constraint for seriesId in the post table is properly defined, as mentioned earlier.

Comment on lines 10 to 12
-- Update Post table to add seriesId column
ALTER TABLE "Post"
ADD COLUMN "seriesId" INTEGER
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Add a foreign key constraint and consider NOT NULL.

The addition of the "seriesId" column to the "Post" table is good. However, there are two points to consider:

  1. It's recommended to add a foreign key constraint to ensure referential integrity between the "Post" and "Series" tables.
  2. Consider whether "seriesId" should be nullable or not, depending on whether all posts must belong to a series.

Here's a suggested modification:

ALTER TABLE "Post"
ADD COLUMN "seriesId" INTEGER,
ADD CONSTRAINT fk_post_series
    FOREIGN KEY ("seriesId")
    REFERENCES "Series" ("id")
    ON DELETE SET NULL;

This adds a foreign key constraint and allows for nullable "seriesId". If you decide that all posts must belong to a series, you can add NOT NULL to the column definition and adjust the ON DELETE behavior accordingly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made this change in the second commit

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RangerCreaky, glad to hear that you've made this change!

^(^‾ʖ‾^)/


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

.input(UpdateSeriesSchema)
.mutation(async ({input, ctx}) => {
const {postId, seriesName} = input;
console.group("series Name: ", seriesName);
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove debug logging statement

The console.group("series Name: ", seriesName); appears to be a debug logging statement. It's recommended to remove this or replace it with appropriate logging for production environments to avoid unnecessary console output.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in the second commit

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RangerCreaky, great! Thank you for addressing that.

(^_^)/


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Comment on lines 33 to 62
const createNewSeries = async (seriesTitle: string) => {
// check if a series with that name already exists
// or else create a new one
let seriesId : number;
const currSeries = await ctx.db.query.series.findFirst({
columns: {
id: true
},
where: (series, { eq }) => eq(series.title, seriesTitle),
})
if(!currSeries){
const [newSeries] = await ctx.db.insert(series).values({
title: seriesTitle,
userId: ctx.session.user.id,
updatedAt: new Date()
}).returning();

seriesId = newSeries.id;
}
else{
seriesId = currSeries.id;
}
// update that series id in the current post
await ctx.db
.update(post)
.set({
seriesId: seriesId
})
.where(eq(post.id, currentPost.id));
}
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Ensure atomicity of database operations with transactions in createNewSeries

The createNewSeries function performs multiple database operations: checking for an existing series, possibly creating a new series, and updating the post's seriesId. To maintain data consistency in case of errors, consider wrapping these operations in a transaction so that all operations either succeed or fail together.

Example using a transaction:

await ctx.db.transaction(async (tx) => {
  // Your database operations here, using tx instead of ctx.db
});

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in the second commit

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RangerCreaky, thanks for making the updates in the second commit!

(_/)
(^_^)


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Comment on lines 63 to 84
const unlinkSeries = async (seriesId: number) => {
// Check if the user has added a another post with the same series id previously
const anotherPostInThisSeries = await ctx.db.query.post.findFirst({
where: (post, { eq, and, ne }) =>
and (
ne(post.id, currentPost.id),
eq(post.seriesId, currentPost.seriesId!)
)
})
// if another post with the same seriesId is present, then do nothing
// else remove the series from the series table
if(!anotherPostInThisSeries){
await ctx.db.delete(series).where(eq(series.id, seriesId));
}
// update that series id in the current post
await ctx.db
.update(post)
.set({
seriesId: null
})
.where(eq(post.id, currentPost.id));
}
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Ensure atomicity of database operations with transactions in unlinkSeries

Similar to createNewSeries, the unlinkSeries function performs multiple database operations that should be atomic. Deleting the series and updating the post's seriesId should be wrapped in a transaction to prevent partial updates if an error occurs.

Example using a transaction:

await ctx.db.transaction(async (tx) => {
  // Your database operations here, using tx instead of ctx.db
});

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RangerCreaky, great! Thanks for making the changes.

(_/)
( •_•)
/>🎉


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Comment on lines 6 to 114
seriesId: true,
userId: true
},
with: {
series: {
columns: {
id: true,
title: true
},
},
},
where: (post, { eq }) => eq(post.id, postId),
});
if (currentPost?.userId !== ctx.session.user.id) {
throw new TRPCError({
code: "FORBIDDEN",
});
}
const createNewSeries = async (seriesTitle: string) => {
// check if a series with that name already exists
// or else create a new one
let seriesId : number;
const currSeries = await ctx.db.query.series.findFirst({
columns: {
id: true
},
where: (series, { eq }) => eq(series.title, seriesTitle),
})
if(!currSeries){
const [newSeries] = await ctx.db.insert(series).values({
title: seriesTitle,
userId: ctx.session.user.id,
updatedAt: new Date()
}).returning();

seriesId = newSeries.id;
}
else{
seriesId = currSeries.id;
}
// update that series id in the current post
await ctx.db
.update(post)
.set({
seriesId: seriesId
})
.where(eq(post.id, currentPost.id));
}
const unlinkSeries = async (seriesId: number) => {
// Check if the user has added a another post with the same series id previously
const anotherPostInThisSeries = await ctx.db.query.post.findFirst({
where: (post, { eq, and, ne }) =>
and (
ne(post.id, currentPost.id),
eq(post.seriesId, currentPost.seriesId!)
)
})
// if another post with the same seriesId is present, then do nothing
// else remove the series from the series table
if(!anotherPostInThisSeries){
await ctx.db.delete(series).where(eq(series.id, seriesId));
}
// update that series id in the current post
await ctx.db
.update(post)
.set({
seriesId: null
})
.where(eq(post.id, currentPost.id));
}

if(seriesName){
// check if the current post is already linked to a series
if(currentPost?.seriesId){
// check if the series title is same as the current series name
// then we do nothing
if(currentPost?.series?.title !== seriesName){
// then the user has updated the series name in this particular edit
// Check if there is another post with the same title, else delete the series
// and create a new post with the new series name
// and update that new series id in the post
await unlinkSeries(currentPost.seriesId);
await createNewSeries(seriesName);
}
}
else{
// the current post is not yet linked to a seriesId
// so create a new series and put that Id in the post
await createNewSeries(seriesName);
}
}
else{
// either the user has not added the series Name (We do nothing)
// or while editing the post, the user has removed the series name
if(currentPost.seriesId !== null){
await unlinkSeries(currentPost.seriesId);
}
}
})
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding unit tests for the update mutation

To ensure the reliability and correctness of the update mutation and its helper functions (createNewSeries and unlinkSeries), consider adding unit tests that cover various scenarios:

  • Creating a new series when adding seriesName to a post.
  • Updating the series when seriesName changes.
  • Removing the series when seriesName is removed from a post.
  • Ensuring that series are deleted only when no other posts are associated.

Would you like assistance in setting up unit tests for this functionality or creating a GitHub issue to track this task?

Comment on lines +50 to +53
updatedAt: timestamp("updatedAt", {
precision: 3,
withTimezone: true
}).notNull()
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add default value and auto-update logic for updatedAt field

The updatedAt field lacks a default value and auto-update logic, which could lead to null values or inaccurate timestamps. To maintain consistency with other tables and ensure accurate timestamps, consider adding these.

Apply this diff to update the updatedAt field:

 updatedAt: timestamp("updatedAt", {
   precision: 3,
   withTimezone: true
 }).notNull()
+  .$onUpdate(() => new Date().toISOString())
+  .default(sql`CURRENT_TIMESTAMP`),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
updatedAt: timestamp("updatedAt", {
precision: 3,
withTimezone: true
}).notNull()
updatedAt: timestamp("updatedAt", {
precision: 3,
withTimezone: true
}).notNull()
.$onUpdate(() => new Date().toISOString())
.default(sql`CURRENT_TIMESTAMP`),

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RangerCreaky, great to hear that you've made the change!

(^_^)/


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Comment on lines 38 to 54
export const series = pgTable("Series", {
id: serial("id").primaryKey(),
title: text("title").notNull(),
description: text("description"),
userId: text("userId"),
createdAt: timestamp("createdAt", {
precision: 3,
mode: "string",
withTimezone: true,
})
.default(sql`CURRENT_TIMESTAMP`)
.notNull(),
updatedAt: timestamp("updatedAt", {
precision: 3,
withTimezone: true
}).notNull()
})
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add foreign key constraint for userId in series table

The userId field in the series table should reference the user table to maintain referential integrity. This ensures that each series is associated with a valid user.

Apply this diff to add the foreign key constraint:

 export const series = pgTable("Series", {
   id: serial("id").primaryKey(),
   title: text("title").notNull(),
   description: text("description"),
-  userId: text("userId"),
+  userId: text("userId")
+    .notNull()
+    .references(() => user.id, { onDelete: "cascade", onUpdate: "cascade" }),
   createdAt: timestamp("createdAt", {
     precision: 3,
     mode: "string",
     withTimezone: true,
   })
     .default(sql`CURRENT_TIMESTAMP`)
     .notNull(),
   updatedAt: timestamp("updatedAt", {
     precision: 3,
     withTimezone: true
   })
-    .notNull()
+    .notNull()
+    .$onUpdate(() => new Date().toISOString())
+    .default(sql`CURRENT_TIMESTAMP`),
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const series = pgTable("Series", {
id: serial("id").primaryKey(),
title: text("title").notNull(),
description: text("description"),
userId: text("userId"),
createdAt: timestamp("createdAt", {
precision: 3,
mode: "string",
withTimezone: true,
})
.default(sql`CURRENT_TIMESTAMP`)
.notNull(),
updatedAt: timestamp("updatedAt", {
precision: 3,
withTimezone: true
}).notNull()
})
export const series = pgTable("Series", {
id: serial("id").primaryKey(),
title: text("title").notNull(),
description: text("description"),
userId: text("userId")
.notNull()
.references(() => user.id, { onDelete: "cascade", onUpdate: "cascade" }),
createdAt: timestamp("createdAt", {
precision: 3,
mode: "string",
withTimezone: true,
})
.default(sql`CURRENT_TIMESTAMP`)
.notNull(),
updatedAt: timestamp("updatedAt", {
precision: 3,
withTimezone: true
})
.notNull()
.$onUpdate(() => new Date().toISOString())
.default(sql`CURRENT_TIMESTAMP`),
});

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in the second commit

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RangerCreaky, thank you for making the update in the second commit!

🐰🎉


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

@@ -215,6 +224,7 @@ const Create = () => {
tags,
canonicalUrl: data.canonicalUrl || undefined,
excerpt: data.excerpt || removeMarkdown(data.body, {}).substring(0, 155),
seriesName: data.seriesName || undefined
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Validate and sanitize seriesName before saving

When adding seriesName to the formData, ensure that the value is properly validated and sanitized. This includes trimming whitespace, limiting the length, and preventing invalid characters. This will help maintain data integrity and prevent potential issues with series management.

Consider adding validation rules using zod or another validation library to enforce these constraints on seriesName.

Comment on lines 239 to 242
await Promise.all([
save({ ...formData, id: postId }),
seriesUpdate({postId, seriesName: formData.seriesName})
]);
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle errors when saving post and updating series concurrently

Using Promise.all to execute save and seriesUpdate mutations concurrently can lead to unhandled errors if one of the promises rejects. It's important to handle each mutation's errors individually to provide clear feedback to the user.

Refactor the code to handle errors for each mutation separately:

-await Promise.all([
-  save({ ...formData, id: postId }),
-  seriesUpdate({ postId, seriesName: formData.seriesName }),
-]);
+try {
+  await save({ ...formData, id: postId });
+} catch (error) {
+  toast.error("Error saving post.");
+  Sentry.captureException(error);
+}
+
+try {
+  await seriesUpdate({ postId, seriesName: formData.seriesName });
+} catch (error) {
+  toast.error("Error updating series.");
+  Sentry.captureException(error);
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await Promise.all([
save({ ...formData, id: postId }),
seriesUpdate({postId, seriesName: formData.seriesName})
]);
try {
await save({ ...formData, id: postId });
} catch (error) {
toast.error("Error saving post.");
Sentry.captureException(error);
}
try {
await seriesUpdate({ postId, seriesName: formData.seriesName });
} catch (error) {
toast.error("Error updating series.");
Sentry.captureException(error);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in the second commit

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RangerCreaky, that's great! Thanks for addressing the issue.

(^o^/)🎉


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Comment on lines 163 to 170
const { mutate: seriesUpdate, status: seriesStatus } = api.series.update.useMutation({
onError(error) {
// TODO: Add error messages from field validations
console.log("Error updating settings: ", error);
toast.error("Error auto-saving");
Sentry.captureException(error);
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove console.log and handle field validation errors

In the seriesUpdate mutation's onError handler, using console.log for error logging is not recommended in production code. Instead, rely on toast.error and Sentry.captureException for error reporting. Additionally, the TODO comment indicates that error messages from field validations need to be added. Please ensure that meaningful error messages are provided to the user when validation errors occur.

Apply this diff to remove the console.log statement and handle validation errors:

 const { mutate: seriesUpdate, status: seriesStatus } = api.series.update.useMutation({
   onError(error) {
     // TODO: Add error messages from field validations
-    console.log("Error updating settings: ", error);
+    // Provide specific error feedback to the user
+    if (error instanceof ZodError) {
+      toast.error(error.issues[0].message);
+    } else {
+      toast.error("Error updating series.");
+    }
     toast.error("Error auto-saving");
     Sentry.captureException(error);
   }
 });

Committable suggestion was skipped due to low confidence.

@RangerCreaky
Copy link
Contributor Author

I am currently going through the coderabbit suggestions,
And will update this PR, after working on those suggestions

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (4)
server/api/router/series.ts (3)

41-74: Well-implemented createNewSeries function with proper transaction handling.

The function correctly uses a database transaction to ensure atomicity when creating or retrieving a series and updating the post. This implementation addresses the previous suggestion to use transactions for maintaining data consistency.

Consider adding error handling within the transaction to catch and handle any potential database errors:

return await ctx.db.transaction(async (tx) => {
  try {
    // Existing transaction code
  } catch (error) {
    console.error('Error in createNewSeries:', error);
    throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Failed to create or update series' });
  }
});

This addition would provide more robust error handling and help with debugging in case of unexpected issues.


76-99: Well-implemented unlinkSeries function with proper transaction handling.

The function correctly uses a database transaction to ensure atomicity when unlinking a series from a post and potentially deleting the series. The logic for checking other posts in the same series before deletion is sound.

As with the createNewSeries function, consider adding error handling within the transaction:

return await ctx.db.transaction(async (tx) => {
  try {
    // Existing transaction code
  } catch (error) {
    console.error('Error in unlinkSeries:', error);
    throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Failed to unlink series' });
  }
});

This addition would provide more robust error handling and help with debugging in case of unexpected issues.


101-127: Well-structured main logic covering all required scenarios.

The implementation successfully handles all the cases mentioned in the PR objectives, including creating, updating, and removing series associations. The flow of execution is clear and follows the expected behavior.

To improve readability, consider extracting the main logic into a separate function:

async function handleSeriesUpdate(currentPost, seriesName) {
  if (seriesName) {
    if (currentPost?.seriesId) {
      if (currentPost?.series?.title !== seriesName) {
        await unlinkSeries(currentPost.seriesId);
        await createNewSeries(seriesName);
      }
    } else {
      await createNewSeries(seriesName);
    }
  } else if (currentPost.seriesId !== null) {
    await unlinkSeries(currentPost.seriesId);
  }
}

// In the main mutation function
await handleSeriesUpdate(currentPost, seriesName);

This refactoring would make the main mutation function more concise and easier to understand at a glance.

server/api/router/post.ts (1)

448-448: LGTM: Series information included in editDraft query

The addition of series: true ensures that series information is fetched when editing a draft, which is consistent with the new series feature. This change supports the EDIT case mentioned in the PR summary.

For improved clarity and consistency with other parts of the code, consider using an object to specify which fields of the series you want to include:

- series: true
+ series: {
+   columns: {
+     id: true,
+     name: true
+   }
+ }

This explicit approach can help prevent over-fetching and makes it clear which series data is being used.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between f0161e6 and 75400e5.

📒 Files selected for processing (5)
  • app/(app)/create/[[...paramsArr]]/_client.tsx (4 hunks)
  • drizzle/0011_add_series_update_post.sql (1 hunks)
  • server/api/router/post.ts (3 hunks)
  • server/api/router/series.ts (1 hunks)
  • server/db/schema.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • drizzle/0011_add_series_update_post.sql
🧰 Additional context used
🔇 Additional comments (11)
server/api/router/series.ts (3)

1-9: LGTM: Imports and router definition are well-structured.

The imports and router definition are appropriate for the functionality being implemented. The use of protectedProcedure ensures that the update mutation is only accessible to authenticated users.


10-40: Great job implementing input validation and error handling!

The code now includes proper input validation for seriesName and handles cases where the post is not found or the user is not authorized. These improvements address the concerns raised in previous reviews and enhance the robustness of the implementation.


1-129: Overall, excellent implementation of the series update functionality.

The code successfully implements the series feature as described in the PR objectives. It handles all the required cases for creating, updating, and removing series associations. The use of database transactions ensures data consistency, and the implementation of input validation and error handling addresses previous review concerns.

The suggested minor improvements for error handling within transactions and code structure refinement will further enhance the robustness and readability of the code.

Great job on addressing the previous review comments and delivering a solid implementation!

server/api/router/post.ts (3)

17-17: LGTM: Import statement updated correctly

The addition of series to the import statement is consistent with the new feature for linking related articles under a specific series.


190-212: LGTM: Deletion logic improved with transaction and series cleanup

The changes effectively address the following:

  1. Wrapping the deletion logic in a transaction ensures atomicity.
  2. Proper cleanup of orphaned series records is implemented.
  3. The logic aligns with the PR objectives for handling series in CREATE, EDIT, and DELETE cases.

These improvements address the concerns raised in the past review comment about ensuring atomicity and preventing race conditions.


Line range hint 1-524: Overall implementation looks good, suggest final verification

The changes effectively implement the series feature as described in the PR objectives. The code addresses the CREATE, EDIT, and DELETE cases for series management, and performance considerations have been taken into account with the use of transactions.

To ensure completeness:

  1. Verify that all edge cases for series management are covered, especially for the EDIT case where a series name might be changed or removed.
  2. Consider adding unit tests to cover the new series-related functionality.
  3. Ensure that the frontend components are updated to utilize the new series information returned by the editDraft query.

To help with verification, you can run the following script to check for any remaining TODOs or FIXMEs related to the series feature:

This will help ensure that all planned tasks for the series feature have been completed.

✅ Verification successful

All checks passed successfully! The series feature implementation is complete with no outstanding TODOs or FIXMEs.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining TODOs or FIXMEs related to the series feature

echo "Checking for remaining TODOs or FIXMEs related to the series feature:"
rg -i "TODO|FIXME" -g "!*.md" | rg -i "series"

Length of output: 195

server/db/schema.ts (3)

38-56: LGTM! The series table is well-defined.

The table structure looks good, with appropriate field types and constraints. The updatedAt field now includes the default value and auto-update logic as suggested in a past review comment.


172-172: LGTM! The seriesId field is correctly added to the post table.

The foreign key constraint has been properly implemented as suggested in a past review comment. The "set null" on delete and "cascade" on update actions are appropriate for this relationship.


298-304: LGTM! The seriesRelations are well-defined.

The relations for the series table have been correctly implemented as suggested in a past review comment. The posts relation allows for multiple posts in a series, and the user relation correctly links a series to its creator.

app/(app)/create/[[...paramsArr]]/_client.tsx (2)

225-225: Validate and sanitize seriesName before saving


566-578: Improve user experience by adding autocomplete for series names

Comment on lines 163 to 169
const { mutate: seriesUpdate, status: seriesStatus } = api.series.update.useMutation({
onError(error) {
toast.error("Error auto-saving");
Sentry.captureException(error);
}
});

Copy link
Contributor

@coderabbitai coderabbitai bot Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve error message for seriesUpdate mutation

In the onError handler for seriesUpdate, the error message displayed is "Error auto-saving." To provide clearer feedback to the user, consider changing it to "Error updating series."

Apply this diff to update the error message:

 const { mutate: seriesUpdate, status: seriesStatus } = api.series.update.useMutation({
     onError(error) {
-      toast.error("Error auto-saving");
+      toast.error("Error updating series.");
       Sentry.captureException(error);
     }
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { mutate: seriesUpdate, status: seriesStatus } = api.series.update.useMutation({
onError(error) {
toast.error("Error auto-saving");
Sentry.captureException(error);
}
});
const { mutate: seriesUpdate, status: seriesStatus } = api.series.update.useMutation({
onError(error) {
toast.error("Error updating series.");
Sentry.captureException(error);
}
});

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RangerCreaky: Great to hear that you've fixed the issue!

(^◡^)/


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Comment on lines 237 to 249
try {
await save({ ...formData, id: postId });
} catch (error) {
toast.error("Error saving post.");
Sentry.captureException(error);
}
try {
await seriesUpdate({ postId, seriesName: formData.seriesName });
toast.success("Saved");
} catch (error) {
toast.error("Error updating series.");
Sentry.captureException(error);
}
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Show success message after both mutations complete successfully

Currently, the success message "Saved" is displayed after the seriesUpdate mutation succeeds, regardless of whether the save mutation was successful. To ensure accurate user feedback, consider displaying the success message only after both save and seriesUpdate mutations have completed successfully.

You can adjust the code as follows:

      let saveSuccess = false;
      try {
        await save({ ...formData, id: postId });
        saveSuccess = true;
      } catch (error) {
        toast.error("Error saving post.");
        Sentry.captureException(error);
      }

      let seriesUpdateSuccess = false;
      try {
        await seriesUpdate({ postId, seriesName: formData.seriesName });
        seriesUpdateSuccess = true;
      } catch (error) {
        toast.error("Error updating series.");
        Sentry.captureException(error);
      }

+     if (saveSuccess && seriesUpdateSuccess) {
+       toast.success("Saved");
+     }

This way, the success message is only shown if both operations succeed.

Committable suggestion was skipped due to low confidence.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in the third commit

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RangerCreaky, thank you for addressing the issue!

(^.^/)


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 75400e5 and ab67084.

📒 Files selected for processing (1)
  • app/(app)/create/[[...paramsArr]]/_client.tsx (4 hunks)
🧰 Additional context used

Comment on lines +577 to +589
<label htmlFor="seriesName">
Series Name
</label>
<input
id="seriesName"
type="text"
placeholder="The name of my series"
defaultValue={data?.series?.title || ""}
{...register("seriesName")}
/>
<p className="mt-2 text-sm text-neutral-600 dark:text-neutral-400">
This text is case-sensitive so make sure you type it exactly as you did in previous articles to ensure they are connected
</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Normalize series names to prevent case-sensitivity issues

Requiring users to type the series name exactly as before due to case sensitivity can lead to inconsistencies and user errors. To enhance user experience and ensure consistent linkage of related articles, consider normalizing series names by converting them to a standard case (e.g., all lowercase) before saving and when querying.

This approach will help prevent issues where articles are not linked correctly because of differences in capitalization, improving the reliability of the series feature.

Comment on lines +237 to +260
let saveSuccess = false;
try {
await save({ ...formData, id: postId });
saveSuccess = true;
} catch (error) {
toast.error("Error saving post.");
Sentry.captureException(error);
}

let seriesUpdateSuccess = false;
try {
if(formData?.seriesName){
await seriesUpdate({ postId, seriesName: formData.seriesName });
}
seriesUpdateSuccess = true;
} catch (error) {
toast.error("Error updating series.");
Sentry.captureException(error);
}

if(saveSuccess && seriesUpdateSuccess){
toast.success("Saved");
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure series update handles series removal correctly

Currently, the seriesUpdate mutation is called only when formData.seriesName exists. This means that if a user removes the series name from a post, the seriesUpdate function is not invoked, and the series association may not be properly removed from the post.

To handle series removal correctly, consider calling seriesUpdate regardless of whether formData.seriesName exists. The mutation can then handle adding, updating, or removing the series association based on the value of seriesName.

Apply this diff to adjust the logic:

          let seriesUpdateSuccess = false;
          try {
-           if(formData?.seriesName){
              await seriesUpdate({ postId, seriesName: formData.seriesName });
-           }
            seriesUpdateSuccess = true;
          } catch (error) {
            toast.error("Error updating series.");
            Sentry.captureException(error);
          }

Ensure that the seriesUpdate mutation on the server side appropriately handles cases where seriesName is undefined or an empty string, removing the series association when necessary.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let saveSuccess = false;
try {
await save({ ...formData, id: postId });
saveSuccess = true;
} catch (error) {
toast.error("Error saving post.");
Sentry.captureException(error);
}
let seriesUpdateSuccess = false;
try {
if(formData?.seriesName){
await seriesUpdate({ postId, seriesName: formData.seriesName });
}
seriesUpdateSuccess = true;
} catch (error) {
toast.error("Error updating series.");
Sentry.captureException(error);
}
if(saveSuccess && seriesUpdateSuccess){
toast.success("Saved");
}
let saveSuccess = false;
try {
await save({ ...formData, id: postId });
saveSuccess = true;
} catch (error) {
toast.error("Error saving post.");
Sentry.captureException(error);
}
let seriesUpdateSuccess = false;
try {
await seriesUpdate({ postId, seriesName: formData.seriesName });
seriesUpdateSuccess = true;
} catch (error) {
toast.error("Error updating series.");
Sentry.captureException(error);
}
if(saveSuccess && seriesUpdateSuccess){
toast.success("Saved");
}

@RangerCreaky
Copy link
Contributor Author

The new PR raised is #1146

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature/ Add "series" to link related articles
1 participant