-
Notifications
You must be signed in to change notification settings - Fork 0
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
Code Cleanup and Query Abstraction #1
base: main
Are you sure you want to change the base?
Conversation
✅ Deploy Preview for y-sweet-supabase-demo ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
📝 Walkthrough📝 WalkthroughWalkthroughThe pull request introduces multiple changes across various components and utility functions in the codebase. Notably, the The 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 10
🧹 Outside diff range and nitpick comments (5)
components/document/editable-doc-title.tsx (1)
Line range hint
33-39
: Consider adding aria-label for accessibility.The Input component could benefit from an aria-label to improve accessibility for screen readers.
<Input value={docMetadata?.name ?? ""} onChange={handleInputChange} className="text-2xl font-bold mb-4 w-fit" placeholder="Untitled Document" + aria-label="Document title" />
components/document/permissions-toggle.tsx (2)
7-11
: Consider adding type safety for the metadata setter.The interface changes look good, moving from a simple boolean to a more comprehensive metadata object improves component flexibility.
Consider making the setter type more specific:
- setDocumentMetadata: (metadata: DocumentMetadata) => void; + setDocumentMetadata: React.Dispatch<React.SetStateAction<DocumentMetadata>>;
Line range hint
35-50
: Improve accessibility of the toggle component.The current implementation lacks proper accessibility support:
- Missing keyboard navigation
- Missing ARIA attributes
- Using div instead of proper button element
Consider these accessibility improvements:
- <div className="flex items-center gap-4"> + <div className="flex items-center gap-4" role="group" aria-labelledby="visibility-label"> - <div>Make this document public</div> + <span id="visibility-label">Make this document public</span> <div className="flex items-center justify-center"> - <div - onClick={() => handleToggle()} + <button + type="button" + onClick={handleToggle} + onKeyPress={(e) => e.key === 'Enter' && handleToggle()} + disabled={isLoading} + aria-pressed={isToggle} + aria-label={`Make document ${isToggle ? 'private' : 'public'}`} className={`w-16 h-8 flex items-center...`} > <div className={`bg-white w-6 h-6 rounded-full...`} ></div> - </div> + </button> </div> </div>app/document/[id]/page.tsx (1)
28-28
: Consider using a more specific error type.While the error state is a good addition, consider using a more specific type than string to handle different error scenarios (e.g., not found, permission denied, etc.) in a type-safe manner.
-const [error, setError] = React.useState(""); +type DocError = { + type: 'not_found' | 'permission_denied' | 'fetch_error'; + message: string; +}; +const [error, setError] = React.useState<DocError | null>(null);README.md (1)
60-60
: LGTM! Consider clarifying the alternative nature of the commands.The addition of an SSH-based git clone command improves accessibility for users who prefer SSH or don't have GitHub CLI installed.
Consider adding "or" between the two commands to make it clear they are alternatives:
```bash gh repo clone jamsocket/y-sweet-supabase-demo
- or
git clone git@github.com:jamsocket/y-sweet-supabase-demo.git</blockquote></details> </blockquote></details> <details> <summary>📜 Review details</summary> **Configuration used: CodeRabbit UI** **Review profile: CHILL** <details> <summary>📥 Commits</summary> Files that changed from the base of the PR and between ad5cb1f4ad56ed2da2b9254a081f15dcea62e169 and 6682e4c91a0567fb44b1fc7d8d5cc1214ed454c1. </details> <details> <summary>📒 Files selected for processing (9)</summary> * README.md (1 hunks) * app/document/[id]/page.tsx (2 hunks) * app/document/page.tsx (0 hunks) * components/create-doc.tsx (1 hunks) * components/display-docs.tsx (1 hunks) * components/document/editable-doc-title.tsx (2 hunks) * components/document/invite-by-email.tsx (3 hunks) * components/document/permissions-toggle.tsx (1 hunks) * utils/supabase/queries.tsx (1 hunks) </details> <details> <summary>💤 Files with no reviewable changes (1)</summary> * app/document/page.tsx </details> <details> <summary>✅ Files skipped from review due to trivial changes (2)</summary> * components/create-doc.tsx * components/display-docs.tsx </details> <details> <summary>🔇 Additional comments (7)</summary><blockquote> <details> <summary>components/document/editable-doc-title.tsx (1)</summary><blockquote> `5-5`: **LGTM! Good abstraction of database operations.** The change to import `editDocTitle` from a centralized queries module improves code organization and maintainability. </blockquote></details> <details> <summary>components/document/invite-by-email.tsx (2)</summary><blockquote> `4-4`: **LGTM! Clean import replacement.** The replacement of `createClient` with `addUserToDoc` improves code organization by abstracting database operations. --- `4-4`: **Verify the implementation of addUserToDoc function.** Let's ensure the imported function is properly implemented with correct error handling. Also applies to: 22-22 <details> <summary>✅ Verification successful</summary> **Function implementation looks correct with proper error handling** The `addUserToDoc` function is well-implemented with: - Proper type checking for input parameters (newEmail: string, id: string) - Error handling for user lookup failures - Error handling for permission insertion failures - Appropriate error messages and return values - Database operations using Supabase client The function follows a good pattern: 1. Looks up user by email 2. Handles user lookup errors 3. Inserts permission record 4. Handles permission insertion errors </details> <details> <summary>🏁 Scripts executed</summary> The following scripts were executed for the analysis: Script: ```shell #!/bin/bash # Description: Verify the implementation of addUserToDoc function # Check the implementation echo "Checking addUserToDoc implementation..." ast-grep --pattern 'export async function addUserToDoc($_: string, $_: string) { $$$ }' # Check error handling echo "Checking error handling..." rg -A 5 "addUserToDoc.*catch"
Length of output: 1634
components/document/permissions-toggle.tsx (1)
4-5
: LGTM! Clean abstraction of database queries.The separation of database queries into a dedicated utility file improves code organization and maintainability.
utils/supabase/queries.tsx (1)
1-4
: LGTM: Server setup is correctThe server-side setup with a single reusable Supabase client instance is appropriate.
app/document/[id]/page.tsx (2)
11-11
: LGTM! Good abstraction of Supabase queries.The change improves separation of concerns by moving database queries to a dedicated utility file.
81-82
: Verify PermissionsToggle component implementation.The prop changes look good, providing more context to the PermissionsToggle component. However, let's verify that the PermissionsToggle component correctly handles these new props.
✅ Verification successful
PermissionsToggle implementation correctly handles the new props
The component implementation in
components/document/permissions-toggle.tsx
properly uses the newdocumentMetadata
andsetDocumentMetadata
props:
- Correctly defines prop types in the
PermissionsToggleProps
interface- Initializes toggle state using
documentMetadata.is_public
- Updates document metadata via
setDocumentMetadata
when visibility changes- Maintains proper type safety with the
DocumentMetadata
typeThe prop changes are well-implemented and working as intended.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify PermissionsToggle component implementation # Test: Check if PermissionsToggle component correctly uses the new props # Look for the PermissionsToggle component definition ast-grep --pattern 'export default function PermissionsToggle({ documentMetadata, setDocumentMetadata }: { documentMetadata: $_; setDocumentMetadata: $_; }) { $$$ }'Length of output: 169
Script:
#!/bin/bash # Let's try a broader search for PermissionsToggle component # First, find the file containing PermissionsToggle fd "PermissionsToggle" --type f # Then let's look at its implementation with ripgrep for better context rg "PermissionsToggle" -A 20Length of output: 4785
.from("docs") // Assuming your table name is "docs" | ||
.update({ name: e.target.value }) // Update the name field | ||
.eq("doc_id", docId); // Match the document by its id | ||
await editDocTitle(docId, e.target.value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix throttling implementation and add error handling.
The current implementation has several issues:
- The throttling logic is ineffective as
inThrottle
is recreated on every call - Missing error handling for failed title updates
Consider this implementation:
export default function EditableDocTitle(props: EditableDocTitleProps) {
const { docId, setDocMetadata, docMetadata } = props;
+ const inThrottle = useRef(false);
+ const [error, setError] = useState<string | null>(null);
+
const handleInputChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
if (docMetadata) {
setDocMetadata({
...docMetadata,
name: e.target.value,
});
}
- let inThrottle = false;
- if (!inThrottle) {
- inThrottle = true;
- await editDocTitle(docId, e.target.value);
+ if (!inThrottle.current) {
+ inThrottle.current = true;
+ try {
+ await editDocTitle(docId, e.target.value);
+ setError(null);
+ } catch (err) {
+ setError('Failed to update title. Please try again.');
+ // Optionally revert to last known good state
+ if (docMetadata) {
+ setDocMetadata({
+ ...docMetadata,
+ name: docMetadata.name,
+ });
+ }
+ }
setTimeout(() => {
- inThrottle = false;
+ inThrottle.current = false;
}, 1000);
}
};
📝 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.
await editDocTitle(docId, e.target.value); | |
export default function EditableDocTitle(props: EditableDocTitleProps) { | |
const { docId, setDocMetadata, docMetadata } = props; | |
const inThrottle = useRef(false); | |
const [error, setError] = useState<string | null>(null); | |
const handleInputChange = async (e: React.ChangeEvent<HTMLInputElement>) => { | |
if (docMetadata) { | |
setDocMetadata({ | |
...docMetadata, | |
name: e.target.value, | |
}); | |
} | |
if (!inThrottle.current) { | |
inThrottle.current = true; | |
try { | |
await editDocTitle(docId, e.target.value); | |
setError(null); | |
} catch (err) { | |
setError('Failed to update title. Please try again.'); | |
// Optionally revert to last known good state | |
if (docMetadata) { | |
setDocMetadata({ | |
...docMetadata, | |
name: docMetadata.name, | |
}); | |
} | |
} | |
setTimeout(() => { | |
inThrottle.current = false; | |
}, 1000); | |
} | |
}; |
@@ -56,7 +44,7 @@ export default function InviteByEmail(props: InviteByEmailProps) { | |||
className="w-full px-4 py-2 border border-gray-300 bg-white rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500" | |||
/> | |||
<button | |||
onClick={() => addPerson()} | |||
onClick={() => inviteUser()} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance accessibility and form handling.
Consider wrapping the input and button in a form element to handle Enter key submissions and improve accessibility.
Here's how to improve it:
-<div>
+<form onSubmit={(e) => {
+ e.preventDefault();
+ inviteUser();
+}}>
Add people by email
<div className="flex items-center py-4">
<input
type="email"
+ aria-label="Email address for invitation"
placeholder="invite-email@gmail.com"
value={newEmail}
onChange={(e) => setNewEmail(e.target.value)}
className="w-full px-4 py-2 border border-gray-300 bg-white rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<button
- onClick={() => inviteUser()}
+ type="submit"
+ aria-label="Send invitation"
className="px-4 py-2 bg-blue-500 text-white rounded-r-md hover:bg-blue-600 focus:outline-none"
>
Add
</button>
</div>
<span className="text-xs text-red-700" role="alert">{error}</span>
-</div>
+</form>
Committable suggestion was skipped due to low confidence.
async function inviteUser() { | ||
const email = newEmail.trim(); | ||
|
||
async function addPerson() { | ||
const { data: user, error: userError } = await supabase | ||
.from("users") | ||
.select("id") | ||
.eq("email", newEmail) | ||
.single(); | ||
if (!email) { | ||
setError("No email provided"); | ||
} else { | ||
const error = await addUserToDoc(email, id); | ||
|
||
if (!user || userError) { | ||
console.error("User not found", userError); | ||
setError("User not found"); | ||
return; | ||
if (error) { | ||
setError(error); | ||
} else { | ||
setToolTipMessage("User added"); | ||
setTimeout(() => setToolTipMessage(""), 2000); | ||
} | ||
} | ||
|
||
// In a secure production environment, this operation should be handled within a server-side action, with proper authorization checks to verify the user's permission to share the specified doc_id. | ||
const { error } = await supabase | ||
.from("permissions") | ||
.insert([{ doc_id: id, user_id: user.id }]) | ||
.select(); | ||
|
||
if (error) { | ||
console.error("Failed to insert permission", error); | ||
setError("Failed to provide permission to user"); | ||
return; | ||
} | ||
setToolTipMessage("User added"); | ||
setTimeout(() => setToolTipMessage(""), 2000); | ||
setNewEmail(""); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider enhancing error handling and user feedback.
While the basic flow is correct, there are several improvements that could be made:
Consider implementing these enhancements:
const [newEmail, setNewEmail] = React.useState("");
const [error, setError] = React.useState("");
+const [isLoading, setIsLoading] = React.useState(false);
+let timeoutId: NodeJS.Timeout;
async function inviteUser() {
+ clearTimeout(timeoutId);
+ setError("");
+ setIsLoading(true);
const email = newEmail.trim();
+
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ if (!emailRegex.test(email)) {
+ setError("Please enter a valid email address");
+ setIsLoading(false);
+ return;
+ }
if (!email) {
setError("No email provided");
+ setIsLoading(false);
} else {
const error = await addUserToDoc(email, id);
if (error) {
setError(error);
} else {
setToolTipMessage("User added");
- setTimeout(() => setToolTipMessage(""), 2000);
+ timeoutId = setTimeout(() => setToolTipMessage(""), 2000);
}
}
setNewEmail("");
+ setIsLoading(false);
}
Then update the button to show loading state:
<button
onClick={() => inviteUser()}
+ disabled={isLoading}
className="px-4 py-2 bg-blue-500 text-white rounded-r-md hover:bg-blue-600 focus:outline-none"
>
- Add
+ {isLoading ? "Adding..." : "Add"}
</button>
📝 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.
async function inviteUser() { | |
const email = newEmail.trim(); | |
async function addPerson() { | |
const { data: user, error: userError } = await supabase | |
.from("users") | |
.select("id") | |
.eq("email", newEmail) | |
.single(); | |
if (!email) { | |
setError("No email provided"); | |
} else { | |
const error = await addUserToDoc(email, id); | |
if (!user || userError) { | |
console.error("User not found", userError); | |
setError("User not found"); | |
return; | |
if (error) { | |
setError(error); | |
} else { | |
setToolTipMessage("User added"); | |
setTimeout(() => setToolTipMessage(""), 2000); | |
} | |
} | |
// In a secure production environment, this operation should be handled within a server-side action, with proper authorization checks to verify the user's permission to share the specified doc_id. | |
const { error } = await supabase | |
.from("permissions") | |
.insert([{ doc_id: id, user_id: user.id }]) | |
.select(); | |
if (error) { | |
console.error("Failed to insert permission", error); | |
setError("Failed to provide permission to user"); | |
return; | |
} | |
setToolTipMessage("User added"); | |
setTimeout(() => setToolTipMessage(""), 2000); | |
setNewEmail(""); | |
const [newEmail, setNewEmail] = React.useState(""); | |
const [error, setError] = React.useState(""); | |
const [isLoading, setIsLoading] = React.useState(false); | |
let timeoutId: NodeJS.Timeout; | |
async function inviteUser() { | |
clearTimeout(timeoutId); | |
setError(""); | |
setIsLoading(true); | |
const email = newEmail.trim(); | |
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; | |
if (!emailRegex.test(email)) { | |
setError("Please enter a valid email address"); | |
setIsLoading(false); | |
return; | |
} | |
if (!email) { | |
setError("No email provided"); | |
setIsLoading(false); | |
} else { | |
const error = await addUserToDoc(email, id); | |
if (error) { | |
setError(error); | |
} else { | |
setToolTipMessage("User added"); | |
timeoutId = setTimeout(() => setToolTipMessage(""), 2000); | |
} | |
} | |
setNewEmail(""); | |
setIsLoading(false); | |
} | |
<button | |
onClick={() => inviteUser()} | |
disabled={isLoading} | |
className="px-4 py-2 bg-blue-500 text-white rounded-r-md hover:bg-blue-600 focus:outline-none" | |
> | |
{isLoading ? "Adding..." : "Add"} | |
</button> |
const { docId, documentMetadata, setDocumentMetadata, setToolTipMessage } = | ||
props; | ||
const [isToggle, setIsToggle] = React.useState(documentMetadata.is_public); // Track toggle state | ||
|
||
const handleToggle = () => { | ||
const handleToggle = async () => { | ||
setIsToggle(!isToggle); // Toggle the state between true and false | ||
setToolTipMessage( | ||
!isToggle ? "Document made public" : "Document made private", | ||
); | ||
|
||
async function updateDocMetadata() { | ||
// Update the document's public status in Supabase | ||
const { data: docsData, error } = await supabase | ||
.from("docs") | ||
.update({ is_public: !isToggle }) // Toggle the is_public field | ||
.eq("doc_id", docId); // Match the document by its id | ||
let error = await changeDocVisibility(!isToggle, docId); | ||
if (!error) { | ||
setDocumentMetadata({ ...documentMetadata, is_public: !isToggle }); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve error handling and state management.
While the async implementation is good, there are a few concerns:
- The toggle state is updated before the API call completes, which could lead to UI/state mismatches if the operation fails
- There's no handling of loading state during the API call
- The error case doesn't revert the toggle state
Consider this improved implementation:
- const [isToggle, setIsToggle] = React.useState(documentMetadata.is_public);
+ const [isLoading, setIsLoading] = React.useState(false);
+ const isToggle = documentMetadata.is_public;
const handleToggle = async () => {
- setIsToggle(!isToggle);
+ if (isLoading) return;
+ setIsLoading(true);
setToolTipMessage(
!isToggle ? "Document made public" : "Document made private"
);
let error = await changeDocVisibility(!isToggle, docId);
if (!error) {
setDocumentMetadata({ ...documentMetadata, is_public: !isToggle });
+ } else {
+ setToolTipMessage("Failed to update document visibility");
}
+ setIsLoading(false);
setTimeout(() => {
setToolTipMessage("");
}, 2000);
};
Also, consider disabling the toggle during loading:
<div
onClick={() => handleToggle()}
- className={`w-16 h-8 flex items-center bg-gray-300 rounded-full p-1 cursor-pointer...`}
+ className={`w-16 h-8 flex items-center bg-gray-300 rounded-full p-1
+ ${isLoading ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}...`}
>
Committable suggestion was skipped due to low confidence.
export async function addUserToDoc(newEmail: string, id: string) { | ||
// In a production environment, you should verify the user's permission to share the specified doc_id. | ||
const { data: user, error: userError } = await supabase | ||
.from("users") | ||
.select("id") | ||
.eq("email", newEmail) | ||
.single(); | ||
|
||
if (!user || userError) { | ||
console.error("User not found", userError); | ||
return "User not found"; | ||
} | ||
|
||
const { error } = await supabase | ||
.from("permissions") | ||
.insert([{ doc_id: id, user_id: user.id }]) | ||
.select(); | ||
|
||
if (error) { | ||
console.error("Failed to insert permission", error); | ||
return "Failed to provide permission to user"; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add transaction and duplicate checks for data integrity
The function has several potential issues that should be addressed:
- The permission insert uses
id
(line 21) but the query parameter isdoc_id
. This mismatch could cause errors. - There's no check for duplicate permissions.
- The user check and permission insert should be wrapped in a transaction to prevent race conditions.
Here's a suggested improvement:
export async function addUserToDoc(newEmail: string, id: string) {
- const { data: user, error: userError } = await supabase
- .from("users")
- .select("id")
- .eq("email", newEmail)
- .single();
+ const { data, error } = await supabase.rpc('add_user_to_doc', {
+ p_email: newEmail,
+ p_doc_id: id
+ });
- if (!user || userError) {
- console.error("User not found", userError);
- return "User not found";
+ if (error) {
+ console.error("Failed to add user to doc", error);
+ return error.message;
}
- const { error } = await supabase
- .from("permissions")
- .insert([{ doc_id: id, user_id: user.id }])
- .select();
-
- if (error) {
- console.error("Failed to insert permission", error);
- return "Failed to provide permission to user";
- }
+ return "User successfully added to document";
}
Create a Postgres function to handle this atomically:
CREATE OR REPLACE FUNCTION add_user_to_doc(p_email TEXT, p_doc_id UUID)
RETURNS void AS $$
BEGIN
INSERT INTO permissions (doc_id, user_id)
SELECT p_doc_id, users.id
FROM users
WHERE users.email = p_email
AND NOT EXISTS (
SELECT 1 FROM permissions
WHERE doc_id = p_doc_id
AND user_id = users.id
);
IF NOT FOUND THEN
RAISE EXCEPTION 'User not found or already has permission';
END IF;
END;
$$ LANGUAGE plpgsql;
export async function editDocTitle(docId: string, newName: string) { | ||
// In a production environment, you should check that the user has permission to edit the doc and add error handling. | ||
await supabase.from("docs").update({ name: newName }).eq("doc_id", docId); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling and input validation
The function needs several improvements for production readiness:
- Missing error handling
- No return value to indicate success/failure
- No input validation for the new title
Here's a suggested improvement:
-export async function editDocTitle(docId: string, newName: string) {
+export async function editDocTitle(docId: string, newName: string): Promise<{ error: string | null }> {
+ if (!newName?.trim()) {
+ return { error: "Document title cannot be empty" };
+ }
+
+ if (newName.length > 255) {
+ return { error: "Document title is too long" };
+ }
+
// In a production environment, you should check that the user has permission to edit the doc and add error handling.
- await supabase.from("docs").update({ name: newName }).eq("doc_id", docId);
+ const { error } = await supabase
+ .from("docs")
+ .update({ name: newName.trim() })
+ .eq("doc_id", docId);
+
+ if (error) {
+ console.error("Failed to update document title:", error);
+ return { error: "Failed to update document title" };
+ }
+
+ return { error: null };
}
📝 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.
export async function editDocTitle(docId: string, newName: string) { | |
// In a production environment, you should check that the user has permission to edit the doc and add error handling. | |
await supabase.from("docs").update({ name: newName }).eq("doc_id", docId); | |
} | |
export async function editDocTitle(docId: string, newName: string): Promise<{ error: string | null }> { | |
if (!newName?.trim()) { | |
return { error: "Document title cannot be empty" }; | |
} | |
if (newName.length > 255) { | |
return { error: "Document title is too long" }; | |
} | |
// In a production environment, you should check that the user has permission to edit the doc and add error handling. | |
const { error } = await supabase | |
.from("docs") | |
.update({ name: newName.trim() }) | |
.eq("doc_id", docId); | |
if (error) { | |
console.error("Failed to update document title:", error); | |
return { error: "Failed to update document title" }; | |
} | |
return { error: null }; | |
} |
export async function changeDocVisibility(isPublic: boolean, docId: string) { | ||
// In a production environment, you should check that the user has permission to edit the doc and add error handling. | ||
const { data, error } = await supabase | ||
.from("docs") | ||
.update({ is_public: isPublic }) | ||
.eq("doc_id", docId); | ||
|
||
if (error) { | ||
console.error("Failed to update doc visibility", error); | ||
return "Failed to update doc visibility"; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve type safety and return value handling
The function could benefit from better type safety and return value handling.
Here's a suggested improvement:
-export async function changeDocVisibility(isPublic: boolean, docId: string) {
+export async function changeDocVisibility(
+ isPublic: boolean,
+ docId: string
+): Promise<{ success: boolean; error: string | null }> {
// In a production environment, you should check that the user has permission to edit the doc and add error handling.
- const { data, error } = await supabase
+ const { error } = await supabase
.from("docs")
.update({ is_public: isPublic })
.eq("doc_id", docId);
if (error) {
console.error("Failed to update doc visibility", error);
- return "Failed to update doc visibility";
+ return { success: false, error: "Failed to update doc visibility" };
}
+
+ return { success: true, error: null };
}
📝 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.
export async function changeDocVisibility(isPublic: boolean, docId: string) { | |
// In a production environment, you should check that the user has permission to edit the doc and add error handling. | |
const { data, error } = await supabase | |
.from("docs") | |
.update({ is_public: isPublic }) | |
.eq("doc_id", docId); | |
if (error) { | |
console.error("Failed to update doc visibility", error); | |
return "Failed to update doc visibility"; | |
} | |
} | |
export async function changeDocVisibility( | |
isPublic: boolean, | |
docId: string | |
): Promise<{ success: boolean; error: string | null }> { | |
// In a production environment, you should check that the user has permission to edit the doc and add error handling. | |
const { error } = await supabase | |
.from("docs") | |
.update({ is_public: isPublic }) | |
.eq("doc_id", docId); | |
if (error) { | |
console.error("Failed to update doc visibility", error); | |
return { success: false, error: "Failed to update doc visibility" }; | |
} | |
return { success: true, error: null }; | |
} |
export async function getDocMetadata(docId: string) { | ||
const { | ||
data: { user }, | ||
} = await supabase.auth.getUser(); | ||
|
||
const { data: docsData, error } = await supabase | ||
.from("docs") | ||
.select("*") | ||
.eq("doc_id", docId); | ||
|
||
if (!docsData || docsData.length === 0 || error) { | ||
console.error("Document not found", error); | ||
return { | ||
data: null, | ||
error: "Document not found", | ||
}; | ||
} | ||
|
||
if (docsData[0]?.is_public) { | ||
return { | ||
data: docsData[0], | ||
error: null, | ||
}; | ||
} else { | ||
const { data: permissionsData, error: permError } = await supabase | ||
.from("permissions") | ||
.select("id") | ||
.eq("doc_id", docsData[0].id) | ||
.eq("user_id", user?.id); | ||
|
||
if (permError || !permissionsData) { | ||
console.error("User does not have access to this document", permError); | ||
return { | ||
data: null, | ||
error: "User does not have access to this document", | ||
}; | ||
} else { | ||
return { | ||
data: docsData[0], | ||
error: null, | ||
}; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Optimize queries and add type safety
The function has several areas for improvement:
- N+1 query issue: Separate queries for docs and permissions
- Inconsistent use of
doc_id
vsid
in queries - Type safety could be improved
Here's a suggested improvement:
+interface DocMetadata {
+ id: string;
+ doc_id: string;
+ name: string;
+ is_public: boolean;
+ created_at: string;
+}
+
+interface DocMetadataResponse {
+ data: DocMetadata | null;
+ error: string | null;
+}
+
-export async function getDocMetadata(docId: string) {
+export async function getDocMetadata(docId: string): Promise<DocMetadataResponse> {
const {
data: { user },
} = await supabase.auth.getUser();
const { data: docsData, error } = await supabase
.from("docs")
- .select("*")
+ .select(`
+ *,
+ permissions!inner(user_id)
+ `)
.eq("doc_id", docId)
+ .eq("permissions.user_id", user?.id)
+ .maybeSingle();
if (!docsData || error) {
console.error("Document not found", error);
return {
data: null,
error: "Document not found",
};
}
- if (docsData[0]?.is_public) {
+ if (docsData.is_public) {
return {
- data: docsData[0],
+ data: docsData,
error: null,
};
} else {
- const { data: permissionsData, error: permError } = await supabase
- .from("permissions")
- .select("id")
- .eq("doc_id", docsData[0].id)
- .eq("user_id", user?.id);
-
- if (permError || !permissionsData) {
- console.error("User does not have access to this document", permError);
- return {
- data: null,
- error: "User does not have access to this document",
- };
- } else {
- return {
- data: docsData[0],
- error: null,
- };
- }
+ return {
+ data: docsData,
+ error: null,
+ };
}
}
This improvement:
- Uses a single query with a join to check permissions
- Adds TypeScript interfaces for better type safety
- Uses
maybeSingle()
to simplify the response handling - Removes redundant permission check for private documents since it's handled by the join
📝 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.
export async function getDocMetadata(docId: string) { | |
const { | |
data: { user }, | |
} = await supabase.auth.getUser(); | |
const { data: docsData, error } = await supabase | |
.from("docs") | |
.select("*") | |
.eq("doc_id", docId); | |
if (!docsData || docsData.length === 0 || error) { | |
console.error("Document not found", error); | |
return { | |
data: null, | |
error: "Document not found", | |
}; | |
} | |
if (docsData[0]?.is_public) { | |
return { | |
data: docsData[0], | |
error: null, | |
}; | |
} else { | |
const { data: permissionsData, error: permError } = await supabase | |
.from("permissions") | |
.select("id") | |
.eq("doc_id", docsData[0].id) | |
.eq("user_id", user?.id); | |
if (permError || !permissionsData) { | |
console.error("User does not have access to this document", permError); | |
return { | |
data: null, | |
error: "User does not have access to this document", | |
}; | |
} else { | |
return { | |
data: docsData[0], | |
error: null, | |
}; | |
} | |
} | |
} | |
interface DocMetadata { | |
id: string; | |
doc_id: string; | |
name: string; | |
is_public: boolean; | |
created_at: string; | |
} | |
interface DocMetadataResponse { | |
data: DocMetadata | null; | |
error: string | null; | |
} | |
export async function getDocMetadata(docId: string): Promise<DocMetadataResponse> { | |
const { | |
data: { user }, | |
} = await supabase.auth.getUser(); | |
const { data: docsData, error } = await supabase | |
.from("docs") | |
.select(` | |
*, | |
permissions!inner(user_id) | |
`) | |
.eq("doc_id", docId) | |
.eq("permissions.user_id", user?.id) | |
.maybeSingle(); | |
if (!docsData || error) { | |
console.error("Document not found", error); | |
return { | |
data: null, | |
error: "Document not found", | |
}; | |
} | |
if (docsData.is_public) { | |
return { | |
data: docsData, | |
error: null, | |
}; | |
} else { | |
return { | |
data: docsData, | |
error: null, | |
}; | |
} | |
} |
app/document/[id]/page.tsx
Outdated
if (!docId) return; | ||
|
||
const { data: docsData } = await supabase | ||
.from("docs") | ||
.select("*") | ||
.eq("doc_id", docId); | ||
let { data: docsData, error } = await getDocMetadata(docId); | ||
|
||
if (!docsData || docsData.length === 0) { | ||
console.error("Document not found"); | ||
if (error || !docsData) { | ||
setError(error ?? "Document not found"); | ||
return; | ||
} | ||
|
||
setDocMetadata({ | ||
name: docsData[0].name ?? "Untitled Document", | ||
id: docsData[0].id, | ||
doc_id: docsData[0].doc_id, | ||
is_public: docsData[0].is_public ?? false, | ||
}); | ||
|
||
if (docsData[0]?.is_public) { | ||
setHasAccess(true); | ||
} else { | ||
const { data: permissionsData, error: permError } = await supabase | ||
.from("permissions") | ||
.select("id") | ||
.eq("doc_id", docsData[0].id) | ||
.eq("user_id", user?.id); | ||
|
||
if (permError || !permissionsData) { | ||
console.error( | ||
"User does not have access to this document", | ||
permError, | ||
); | ||
} else { | ||
setHasAccess(true); | ||
} | ||
if (docsData) { | ||
setDocMetadata({ | ||
name: docsData.name ?? "Untitled Document", | ||
id: docsData.id, | ||
doc_id: docsData.doc_id, | ||
is_public: docsData.is_public ?? false, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve error handling specificity.
While the error handling is functional, it could be more specific about different error cases. Consider distinguishing between:
- Missing document ID
- Document not found
- Permission errors
if (!docId) {
- return;
+ setError({
+ type: 'not_found',
+ message: 'Invalid document ID'
+ });
+ return;
}
let { data: docsData, error } = await getDocMetadata(docId);
if (error || !docsData) {
- setError(error ?? "Document not found");
+ setError({
+ type: error ? 'fetch_error' : 'not_found',
+ message: error ?? "Document not found"
+ });
return;
}
Committable suggestion was skipped due to low confidence.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
🧹 Outside diff range and nitpick comments (5)
components/document/create-doc.tsx (2)
11-11
: Add TypeScript types for better type safetyThe destructured response from
createDoc()
lacks type information.Consider adding proper typing:
- let {data, error} = await createDoc(); + let {data, error}: {data: string | null; error: Error | null} = await createDoc();
1-6
: Consider adding proper imports for error handlingYou'll need to import additional dependencies for the suggested improvements.
Add these imports:
"use client"; import React from "react"; +import { useState } from "react"; import { Button } from "../ui/button"; import { useRouter } from "next/navigation"; import { createDoc } from "@/utils/supabase/queries"; +import { toast } from "@/components/ui/toast";components/document/display-docs.tsx (1)
13-25
: Consider using SWR or React Query for better data management.The current implementation could benefit from a more robust data fetching strategy.
Consider these improvements:
- Use SWR or React Query for automatic:
- Caching
- Revalidation
- Error retry
- Loading states
- Implement error boundaries for better error handling
Example with SWR:
import useSWR from 'swr' export default function DisplayDocs() { const { data: docs, error, isLoading } = useSWR<Docs[]>('docs', getDocs) // ... rest of the component }app/document/[id]/page.tsx (1)
32-47
: Consider adding specific error message for missing docId.The early return for missing docId should set an error message to provide better user feedback.
-if (!docId) return; +if (!docId) { + setError("Invalid document ID"); + return; +}utils/supabase/queries.tsx (1)
144-147
: Improve type safety by defining an interface fordoc
Currently, you're using
(doc: any)
in your mapping function, which reduces type safety. Consider defining an interface for your document objects to enhance type checking and maintainability.Apply this diff to define an interface and use it:
+interface Doc { + doc_id: string; + name: string | null; +} const transformedDocs = docsData?.map((doc: Doc) => ({ doc_id: doc.doc_id, name: doc.name ?? "Untitled Document", }));
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (8)
- app/api/docs/route.ts (0 hunks)
- app/document/[id]/page.tsx (2 hunks)
- app/document/page.tsx (1 hunks)
- components/create-doc.tsx (0 hunks)
- components/display-docs.tsx (0 hunks)
- components/document/create-doc.tsx (1 hunks)
- components/document/display-docs.tsx (1 hunks)
- utils/supabase/queries.tsx (1 hunks)
💤 Files with no reviewable changes (3)
- app/api/docs/route.ts
- components/create-doc.tsx
- components/display-docs.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- app/document/page.tsx
🔇 Additional comments (4)
app/document/[id]/page.tsx (4)
11-11
: LGTM! Good abstraction of database queries.Moving the database query logic to a dedicated utility file improves code organization and maintainability.
28-28
: LGTM! State management simplified.Good removal of unnecessary state and addition of focused error handling state.
54-55
: Previous suggestion for enhanced error presentation still applies.
81-82
: LGTM! Verify PermissionsToggle component interface.The change from primitive props to passing the complete metadata object is a good practice.
✅ Verification successful
Let's search for the component file and then analyze its props interface.
Props interface matches the usage - all good!
The PermissionsToggle component correctly defines the
documentMetadata
andsetDocumentMetadata
props in its interface, matching exactly how they are being used in the page component.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Verify PermissionsToggle component accepts the new props ast-grep --pattern 'type $Props = { $$$ documentMetadata: $_ setDocumentMetadata: $_ $$$ }'Length of output: 101
Script:
#!/bin/bash # Let's try to find the PermissionsToggle component definition first fd "PermissionsToggle" --type f # Then we can examine its content rg -l "PermissionsToggle" --type tsxLength of output: 96
Script:
#!/bin/bash # Let's try without file type restriction first rg -l "PermissionsToggle" # Also search for the component definition pattern rg "interface.*PermissionsToggle|type.*PermissionsToggle"Length of output: 233
Script:
#!/bin/bash # Let's examine the PermissionsToggleProps interface rg -A 5 "interface PermissionsToggleProps" components/document/permissions-toggle.tsxLength of output: 288
components/document/create-doc.tsx
Outdated
router.push(`/document/${data}`); | ||
}; | ||
|
||
return <Button onClick={() => onCreateDoc()}>+ Create new doc</Button> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Optimize event handler binding
The current onClick handler creates a new function on every render.
Directly pass the function reference:
- return <Button onClick={() => onCreateDoc()}>+ Create new doc</Button>
+ return <Button onClick={onCreateDoc}>+ Create new doc</Button>
📝 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.
return <Button onClick={() => onCreateDoc()}>+ Create new doc</Button> | |
return <Button onClick={onCreateDoc}>+ Create new doc</Button> |
components/document/create-doc.tsx
Outdated
const onCreateDoc = async () => { | ||
let {data, error} = await createDoc(); | ||
|
||
if (error) { | ||
console.error(error); | ||
return; | ||
} | ||
|
||
router.push(`/document/${data}`); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enhance error handling and user feedback
The current error handling only logs to console, which doesn't provide feedback to users when document creation fails.
Consider implementing proper error handling:
const onCreateDoc = async () => {
+ try {
let {data, error} = await createDoc();
if (error) {
- console.error(error);
- return;
+ throw error;
}
router.push(`/document/${data}`);
+ } catch (error) {
+ console.error(error);
+ // Add toast or alert for user feedback
+ toast.error("Failed to create document. Please try again.");
}
};
Committable suggestion was skipped due to low confidence.
components/document/create-doc.tsx
Outdated
export default function CreateDoc() { | ||
const router = useRouter(); | ||
|
||
const onCreateDoc = async () => { | ||
let {data, error} = await createDoc(); | ||
|
||
if (error) { | ||
console.error(error); | ||
return; | ||
} | ||
|
||
router.push(`/document/${data}`); | ||
}; | ||
|
||
return <Button onClick={() => onCreateDoc()}>+ Create new doc</Button> | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add loading state and prevent duplicate submissions
The button remains clickable during document creation, which could lead to duplicate submissions.
Implement loading state and disable the button during submission:
export default function CreateDoc() {
const router = useRouter();
+ const [isLoading, setIsLoading] = useState(false);
const onCreateDoc = async () => {
+ if (isLoading) return;
+ setIsLoading(true);
try {
let {data, error} = await createDoc();
if (error) throw error;
router.push(`/document/${data}`);
} catch (error) {
console.error(error);
toast.error("Failed to create document");
+ } finally {
+ setIsLoading(false);
}
};
- return <Button onClick={() => onCreateDoc()}>+ Create new doc</Button>
+ return (
+ <Button
+ onClick={onCreateDoc}
+ disabled={isLoading}
+ >
+ {isLoading ? "Creating..." : "+ Create new doc"}
+ </Button>
+ );
}
📝 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.
export default function CreateDoc() { | |
const router = useRouter(); | |
const onCreateDoc = async () => { | |
let {data, error} = await createDoc(); | |
if (error) { | |
console.error(error); | |
return; | |
} | |
router.push(`/document/${data}`); | |
}; | |
return <Button onClick={() => onCreateDoc()}>+ Create new doc</Button> | |
} | |
export default function CreateDoc() { | |
const router = useRouter(); | |
const [isLoading, setIsLoading] = useState(false); | |
const onCreateDoc = async () => { | |
if (isLoading) return; | |
setIsLoading(true); | |
try { | |
let {data, error} = await createDoc(); | |
if (error) throw error; | |
router.push(`/document/${data}`); | |
} catch (error) { | |
console.error(error); | |
toast.error("Failed to create document"); | |
} finally { | |
setIsLoading(false); | |
} | |
}; | |
return ( | |
<Button | |
onClick={onCreateDoc} | |
disabled={isLoading} | |
> | |
{isLoading ? "Creating..." : "+ Create new doc"} | |
</Button> | |
); | |
} |
name: string; | ||
}; | ||
export default function DisplayDocs() { | ||
const [docs, setDocs] = useState<Docs[]>([]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enhance state management and error handling.
The current implementation has several areas for improvement:
- Remove debug console.log
- Add loading state for better UX
- Add error state for user feedback
Here's the suggested implementation:
- const [docs, setDocs] = useState<Docs[]>([]);
+ const [docs, setDocs] = useState<Docs[]>([]);
+ const [isLoading, setIsLoading] = useState(true);
+ const [error, setError] = useState<string | null>(null);
useEffect(() => {
async function fetchDocs() {
+ setIsLoading(true);
+ setError(null);
let {data, error} = await getDocs();
- console.log(data, error)
if(error || !data) {
- console.error(error);
+ setError(error?.message ?? 'Failed to fetch documents');
+ setIsLoading(false);
return;
}
setDocs(data);
+ setIsLoading(false);
}
fetchDocs()
}, []);
Also applies to: 13-25
type Docs = { | ||
doc_id: string; | ||
name: string; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider enhancing the Docs type definition.
The current type definition could be more comprehensive to support future features and improve data display.
Consider adding useful metadata fields:
type Docs = {
doc_id: string;
name: string;
+ created_at?: string;
+ updated_at?: string;
+ owner_id?: string;
};
📝 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.
type Docs = { | |
doc_id: string; | |
name: string; | |
}; | |
type Docs = { | |
doc_id: string; | |
name: string; | |
created_at?: string; | |
updated_at?: string; | |
owner_id?: string; | |
}; |
return ( | ||
<div className="w-full"> | ||
<h2 className="text-2xl font-bold mb-4 w-fit">Recent Documents</h2> | ||
<div className="flex flex-col gap-4"> | ||
{docs.length === 0 ? ( | ||
<p className="text-gray-500"> | ||
No documents found. Start by creating a new document! | ||
</p> | ||
) : ( | ||
docs.map((doc) => ( | ||
<Link | ||
href={`/document/${doc.doc_id}`} | ||
key={doc.doc_id} | ||
className="w-full flex justify-between items-center gap-3 py-1 border-b border-neutral-800 text-neutral-300 hover:text-white hover:border-neutral-200 transition-all" | ||
> | ||
<span>{doc.name ?? "Untitled Document"}</span> | ||
<span>{`->`}</span> | ||
</Link> | ||
)) | ||
)} | ||
</div> | ||
</div> | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance UI feedback and accessibility.
The current UI could be improved with loading states, error feedback, and better accessibility.
Consider these improvements:
return (
<div className="w-full">
<h2 className="text-2xl font-bold mb-4 w-fit">Recent Documents</h2>
<div className="flex flex-col gap-4">
+ {isLoading && (
+ <p className="text-gray-500">Loading documents...</p>
+ )}
+ {error && (
+ <p className="text-red-500" role="alert">{error}</p>
+ )}
- {docs.length === 0 ? (
+ {!isLoading && !error && docs.length === 0 ? (
<p className="text-gray-500">
No documents found. Start by creating a new document!
</p>
) : (
docs.map((doc) => (
<Link
href={`/document/${doc.doc_id}`}
key={doc.doc_id}
+ aria-label={`Open ${doc.name ?? "Untitled Document"}`}
className="w-full flex justify-between items-center gap-3 py-1 border-b border-neutral-800 text-neutral-300 hover:text-white hover:border-neutral-200 transition-all"
>
<span>{doc.name ?? "Untitled Document"}</span>
- <span>{`->`}</span>
+ <span aria-hidden="true">→</span>
</Link>
))
)}
</div>
</div>
);
Committable suggestion was skipped due to low confidence.
utils/supabase/queries.tsx
Outdated
const { data: docsData, error: docsError } = await supabase | ||
.from("docs") | ||
.select("id, doc_id, is_public, name") | ||
.in("id", docIds); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential mismatch between doc_id
s and id
s in document query
In your getDocs
function, the docIds
array contains doc_id
s from the permissions
table. However, when querying the docs
table, you are using .in("id", docIds)
. If docs.id
does not correspond to the doc_id
s from permissions
, this could lead to incorrect documents being retrieved. Consider verifying whether you should be using .in("doc_id", docIds)
instead.
Apply this diff to correct the query:
.from("docs")
.select("id, doc_id, is_public, name")
- .in("id", docIds);
+ .in("doc_id", docIds);
📝 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.
const { data: docsData, error: docsError } = await supabase | |
.from("docs") | |
.select("id, doc_id, is_public, name") | |
.in("id", docIds); | |
const { data: docsData, error: docsError } = await supabase | |
.from("docs") | |
.select("id, doc_id, is_public, name") | |
.in("doc_id", docIds); |
utils/supabase/queries.tsx
Outdated
export async function createDoc() { | ||
const ysweetDoc = await createYSweetDoc( | ||
process.env.Y_SWEET_CONNECTION_STRING ?? "", | ||
); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling for createYSweetDoc
call
Currently, the call to createYSweetDoc
does not handle potential errors. If the function fails (e.g., due to an invalid connection string), it could cause an unhandled exception. Consider wrapping the call in a try-catch block to handle any errors gracefully.
Apply this diff to add error handling:
export async function createDoc() {
- const ysweetDoc = await createYSweetDoc(
- process.env.Y_SWEET_CONNECTION_STRING ?? "",
- );
+ let ysweetDoc;
+ try {
+ ysweetDoc = await createYSweetDoc(
+ process.env.Y_SWEET_CONNECTION_STRING ?? "",
+ );
+ } catch (error) {
+ console.error("Failed to create Y-Sweet document:", error);
+ return {
+ data: null,
+ error: "Failed to create Y-Sweet document"
+ };
+ }
Committable suggestion was skipped due to low confidence.
utils/supabase/queries.tsx
Outdated
const { data: docData, error: docError } = await supabase | ||
.from("docs") | ||
.insert([{ doc_id: ysweetDoc.docId }]) | ||
.select(); | ||
|
||
if (docError) { | ||
return { | ||
data: null, | ||
error: `Failed to insert doc: ${docError.message}`} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add null check for docData
before accessing docData[0]
In the createDoc
function, after inserting into the docs
table, you access docData[0].id
without ensuring that docData
contains data. If the insert operation does not return data, accessing docData[0]
could result in an error. Consider adding a check to ensure docData
is not null or empty before accessing it.
Apply this diff to add the check:
const { data: docData, error: docError } = await supabase
.from("docs")
.insert([{ doc_id: ysweetDoc.docId }])
.select();
- if (docError) {
+ if (docError || !docData || docData.length === 0) {
return {
data: null,
- error: `Failed to insert doc: ${docError.message}`}
+ error: `Failed to insert doc: ${docError ? docError.message : 'No data returned from insert'}`
}
}
📝 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.
const { data: docData, error: docError } = await supabase | |
.from("docs") | |
.insert([{ doc_id: ysweetDoc.docId }]) | |
.select(); | |
if (docError) { | |
return { | |
data: null, | |
error: `Failed to insert doc: ${docError.message}`} | |
} | |
const { data: docData, error: docError } = await supabase | |
.from("docs") | |
.insert([{ doc_id: ysweetDoc.docId }]) | |
.select(); | |
if (docError || !docData || docData.length === 0) { | |
return { | |
data: null, | |
error: `Failed to insert doc: ${docError ? docError.message : 'No data returned from insert'}` | |
} | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (1)
utils/supabase/queries.tsx (1)
1-5
: Add type safety to Supabase client initializationConsider adding type definitions for your database schema to improve type safety.
import { Database } from './types' const supabase = createClient<Database>();
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (3)
- components/document/create-doc.tsx (1 hunks)
- components/document/display-docs.tsx (1 hunks)
- utils/supabase/queries.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- components/document/create-doc.tsx
- components/document/display-docs.tsx
🔇 Additional comments (6)
utils/supabase/queries.tsx (6)
7-29
: Previous review comment is still applicableThe concerns about transactions and duplicate checks raised in the previous review remain valid.
31-34
: Previous review comment is still applicableThe concerns about error handling and input validation raised in the previous review remain valid.
36-47
: Previous review comment is still applicableThe concerns about type safety and return value handling raised in the previous review remain valid.
49-92
: Previous review comment is still applicableThe concerns about query optimization and type safety raised in the previous review remain valid.
129-133
: Previous review comment is still applicableThe concern about potential mismatch between doc_id and id in the query remains valid.
153-198
: Previous review comments are still applicableThe concerns about error handling for createYSweetDoc and null checks for docData raised in the previous reviews remain valid.
const transformedDocs = docsData?.map((doc: any) => ({ | ||
doc_id: doc.doc_id, | ||
name: doc.name ?? "Untitled Document", | ||
})); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add type safety to document transformation
The current implementation uses any
type which bypasses TypeScript's type checking. Consider defining proper interfaces for your document types.
+ interface Doc {
+ id: string;
+ doc_id: string;
+ is_public: boolean;
+ name: string | null;
+ }
+
+ interface TransformedDoc {
+ doc_id: string;
+ name: string;
+ }
- const transformedDocs = docsData?.map((doc: any) => ({
+ const transformedDocs = docsData?.map((doc: Doc): TransformedDoc => ({
doc_id: doc.doc_id,
name: doc.name ?? "Untitled Document",
}));
📝 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.
const transformedDocs = docsData?.map((doc: any) => ({ | |
doc_id: doc.doc_id, | |
name: doc.name ?? "Untitled Document", | |
})); | |
interface Doc { | |
id: string; | |
doc_id: string; | |
is_public: boolean; | |
name: string | null; | |
} | |
interface TransformedDoc { | |
doc_id: string; | |
name: string; | |
} | |
const transformedDocs = docsData?.map((doc: Doc): TransformedDoc => ({ | |
doc_id: doc.doc_id, | |
name: doc.name ?? "Untitled Document", | |
})); |
No description provided.