From d0e76c85ce3b0c20209cd4c2340d074740dd8840 Mon Sep 17 00:00:00 2001
From: Kamran Ahmed
Date: Fri, 16 Aug 2024 12:59:14 +0100
Subject: [PATCH] Requirement verification functionality
---
.../Projects/SubmissionRequirement.tsx | 4 +-
.../Projects/SubmitProjectModal.tsx | 128 ++++++++++++++----
2 files changed, 107 insertions(+), 25 deletions(-)
diff --git a/src/components/Projects/SubmissionRequirement.tsx b/src/components/Projects/SubmissionRequirement.tsx
index a8946aaff..455f7d084 100644
--- a/src/components/Projects/SubmissionRequirement.tsx
+++ b/src/components/Projects/SubmissionRequirement.tsx
@@ -1,6 +1,6 @@
import type { ReactNode } from 'react';
import { cn } from '../../lib/classname.ts';
-import { CheckIcon, CircleDashed } from 'lucide-react';
+import {CheckIcon, CircleDashed, X} from 'lucide-react';
type SubmissionRequirementProps = {
status: 'pending' | 'success' | 'error';
@@ -23,7 +23,7 @@ export function SubmissionRequirement(props: SubmissionRequirementProps) {
) : status === 'success' ? (
) : (
-
+
)}
{children}
diff --git a/src/components/Projects/SubmitProjectModal.tsx b/src/components/Projects/SubmitProjectModal.tsx
index c9f271eb4..1bcece5da 100644
--- a/src/components/Projects/SubmitProjectModal.tsx
+++ b/src/components/Projects/SubmitProjectModal.tsx
@@ -1,4 +1,4 @@
-import { CheckIcon, CircleDashed, X } from 'lucide-react';
+import { CheckIcon, CircleDashed, CopyIcon, X } from 'lucide-react';
import { Modal } from '../Modal';
import { useState, type FormEvent, type ReactNode } from 'react';
import { useToast } from '../../hooks/use-toast';
@@ -6,12 +6,19 @@ import { httpPost } from '../../lib/http';
import { GitHubIcon } from '../ReactIcons/GitHubIcon.tsx';
import { cn } from '../../lib/classname.ts';
import { SubmissionRequirement } from './SubmissionRequirement.tsx';
+import { useCopyText } from '../../hooks/use-copy-text.ts';
type SubmitProjectResponse = {
repositoryUrl: string;
submittedAt: Date;
};
+type VerificationChecksType = {
+ repositoryExists: 'pending' | 'success' | 'error';
+ readmeExists: 'pending' | 'success' | 'error';
+ projectUrlExists: 'pending' | 'success' | 'error';
+};
+
type SubmitProjectModalProps = {
onClose: () => void;
projectId: string;
@@ -27,19 +34,40 @@ export function SubmitProjectModal(props: SubmitProjectModalProps) {
repositoryUrl: defaultRepositoryUrl = '',
} = props;
+ const { isCopied, copyText } = useCopyText();
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState('');
const [successMessage, setSuccessMessage] = useState('');
const [repoUrl, setRepoUrl] = useState(defaultRepositoryUrl);
+ const [verificationChecks, setVerificationChecks] =
+ useState({
+ repositoryExists: 'pending',
+ readmeExists: 'pending',
+ projectUrlExists: 'pending',
+ });
+
+ const projectUrl = `${import.meta.env.DEV ? 'http://localhost:3000' : 'https://roadmap.sh'}/projects/${projectId}`;
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
try {
+ setVerificationChecks({
+ repositoryExists: 'pending',
+ readmeExists: 'pending',
+ projectUrlExists: 'pending',
+ });
+
setIsLoading(true);
setError('');
setSuccessMessage('');
if (!repoUrl) {
+ setVerificationChecks({
+ repositoryExists: 'error',
+ readmeExists: 'pending',
+ projectUrlExists: 'pending',
+ });
+
throw new Error('Repository URL is required');
}
@@ -50,6 +78,12 @@ export function SubmitProjectModal(props: SubmitProjectModalProps) {
const repoName = repoUrlParts[2];
if (!username || !repoName) {
+ setVerificationChecks({
+ repositoryExists: 'error',
+ readmeExists: 'pending',
+ projectUrlExists: 'pending',
+ });
+
throw new Error('Invalid GitHub repository URL');
}
@@ -58,6 +92,12 @@ export function SubmitProjectModal(props: SubmitProjectModalProps) {
const allContentsUrl = `${mainApiUrl}/contents`;
const allContentsResponse = await fetch(allContentsUrl);
if (!allContentsResponse.ok) {
+ setVerificationChecks({
+ repositoryExists: 'error',
+ readmeExists: 'pending',
+ projectUrlExists: 'pending',
+ });
+
if (allContentsResponse?.status === 404) {
throw new Error(
'Repository not found. Make sure it exists and is public.',
@@ -69,6 +109,12 @@ export function SubmitProjectModal(props: SubmitProjectModalProps) {
const allContentsData = await allContentsResponse.json();
if (!Array.isArray(allContentsData)) {
+ setVerificationChecks({
+ repositoryExists: 'error',
+ readmeExists: 'pending',
+ projectUrlExists: 'pending',
+ });
+
throw new Error('Failed to fetch repository contents');
}
@@ -76,24 +122,47 @@ export function SubmitProjectModal(props: SubmitProjectModalProps) {
(file) => file.name.toLowerCase() === 'readme.md',
);
if (!readmeFile || !readmeFile.url) {
+ setVerificationChecks({
+ repositoryExists: 'success',
+ readmeExists: 'error',
+ projectUrlExists: 'pending',
+ });
+
throw new Error('Readme file not found');
}
const readmeUrl = readmeFile.url;
const response = await fetch(readmeUrl);
if (!response.ok || response.status === 404) {
+ setVerificationChecks({
+ repositoryExists: 'success',
+ readmeExists: 'error',
+ projectUrlExists: 'pending',
+ });
+
throw new Error('Readme file not found');
}
const data = await response.json();
if (!data.content) {
+ setVerificationChecks({
+ repositoryExists: 'success',
+ readmeExists: 'error',
+ projectUrlExists: 'pending',
+ });
+
throw new Error('Readme file not found');
}
const readmeContent = window.atob(data.content);
- const projectUrl = `${window.location.origin}/projects/${projectId}`;
if (!readmeContent.includes(projectUrl)) {
- throw new Error('Project URL not found in the readme file');
+ setVerificationChecks({
+ repositoryExists: 'success',
+ readmeExists: 'success',
+ projectUrlExists: 'error',
+ });
+
+ throw new Error('Add the project page URL to the readme file');
}
const submitProjectUrl = `${import.meta.env.PUBLIC_API_URL}/v1-submit-project/${projectId}`;
@@ -108,6 +177,7 @@ export function SubmitProjectModal(props: SubmitProjectModalProps) {
setSuccessMessage('Repository verified successfully');
setIsLoading(false);
+
onSubmit(submitResponse);
} catch (error: any) {
console.error(error);
@@ -116,21 +186,6 @@ export function SubmitProjectModal(props: SubmitProjectModalProps) {
}
};
- const verificationChecks = {
- valid_url: {
- status: 'pending',
- message: 'URL must point to a public GitHub repository',
- },
- valid_repo: {
- status: 'pending',
- message: 'Repository must contain a readme file',
- },
- valid_readme: {
- status: 'pending',
- message: 'Readme file must contain the project URL',
- },
- };
-
return (
@@ -141,21 +196,48 @@ export function SubmitProjectModal(props: SubmitProjectModalProps) {
-
+
URL must point to a public GitHub repository
-
+
Repository must contain a README file
-
- README file must contain the project URL
+
+ README file must contain the{' '}
+