bookwiz.io / app / auth / github / create-repo / page.tsx
page.tsx
Raw
'use client'

import { useEffect, useState, Suspense } from 'react'
import { useRouter, useSearchParams } from 'next/navigation'
import { supabase } from '@/lib/supabase'
import { IoLogoGithub } from 'react-icons/io5'

function CreateRepoContent() {
  const router = useRouter()
  const searchParams = useSearchParams()
  const [repoName, setRepoName] = useState('')
  const [description, setDescription] = useState('')
  const [isPrivate, setIsPrivate] = useState(true)
  const [isCreating, setIsCreating] = useState(false)
  const [error, setError] = useState<string | null>(null)

  const bookId = searchParams.get('bookId')
  const accessToken = searchParams.get('accessToken')
  const githubUsername = searchParams.get('githubUsername')
  const redirectUrl = searchParams.get('redirectUrl') || '/dashboard'

  useEffect(() => {
    if (!bookId || !accessToken) {
      router.push('/dashboard?error=Missing authorization data')
    }
  }, [bookId, accessToken, router])

  const handleCreateRepository = async (e: React.FormEvent) => {
    e.preventDefault()
    
    if (!repoName.trim() || !accessToken || !bookId) return
    
    setIsCreating(true)
    setError(null)

    try {
      // Create repository via GitHub API
      const response = await fetch('https://api.github.com/user/repos', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${accessToken}`,
          'Accept': 'application/vnd.github.v3+json',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          name: repoName,
          description: description || undefined,
          private: isPrivate,
          auto_init: true
        }),
      })

      if (!response.ok) {
        const errorData = await response.json()
        throw new Error(errorData.message || 'Failed to create repository')
      }

      const newRepo = await response.json()

      // Get current user
      const { data: { user } } = await supabase.auth.getUser()
      if (!user) {
        throw new Error('Please log in first')
      }

      // Create GitHub integration
      const integration = {
        provider: 'github',
        repository_url: newRepo.html_url,
        repository_name: newRepo.name,
        repository_full_name: newRepo.full_name,
        access_token: accessToken,
        github_username: githubUsername,
        repository_id: newRepo.id,
        is_private: newRepo.private,
        user_id: user.id
      }

      const integrationResponse = await fetch(`/api/books/${bookId}/github-integration`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ integration }),
      })

      if (!integrationResponse.ok) {
        const errorData = await integrationResponse.json()
        throw new Error(errorData.error || 'Failed to connect repository')
      }

      // Success! Redirect back to the book
      const successUrl = new URL(redirectUrl, window.location.origin)
      successUrl.searchParams.set('success', `Created and connected repository ${newRepo.full_name}`)
      router.push(successUrl.toString())

    } catch (error) {
      setError(error instanceof Error ? error.message : 'Failed to create repository')
    } finally {
      setIsCreating(false)
    }
  }

  if (!bookId || !accessToken) {
    return (
      <div className="min-h-screen flex items-center justify-center bg-slate-900">
        <div className="text-center bg-slate-800 p-8 rounded-lg border border-slate-700">
          <h1 className="text-2xl font-bold text-red-400 mb-2">Authorization Error</h1>
          <p className="text-slate-400 mb-4">Missing required authorization data</p>
          <button 
            onClick={() => router.push('/dashboard')}
            className="bg-teal-600 text-white px-4 py-2 rounded-lg hover:bg-teal-700 transition-colors"
          >
            Return to Dashboard
          </button>
        </div>
      </div>
    )
  }

  return (
    <div className="min-h-screen bg-slate-900 py-8">
      <div className="max-w-2xl mx-auto px-4">
        <div className="text-center mb-8">
          <div className="flex items-center justify-center gap-3 mb-4">
            <IoLogoGithub className="w-8 h-8 text-slate-200" />
            <h1 className="text-2xl font-semibold text-slate-200">
              Create New Repository
            </h1>
          </div>
          <p className="text-slate-400">
            Create a new GitHub repository for your book
          </p>
          {githubUsername && (
            <div className="mt-2 inline-block bg-slate-800 border border-slate-700 px-3 py-1 rounded-full text-sm text-slate-300">
              @{githubUsername}
            </div>
          )}
        </div>

        <div className="bg-slate-800/50 rounded-lg border border-slate-700 p-6">
          <form onSubmit={handleCreateRepository} className="space-y-6">
            <div>
              <label htmlFor="repoName" className="block text-sm font-medium text-slate-300 mb-2">
                Repository Name *
              </label>
              <input
                type="text"
                id="repoName"
                value={repoName}
                onChange={(e) => setRepoName(e.target.value)}
                placeholder="my-awesome-book"
                required
                className="w-full px-4 py-2 bg-slate-800 border border-slate-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-teal-500 text-slate-200 placeholder-slate-500"
              />
              <p className="text-xs text-slate-500 mt-1">
                Use lowercase letters, numbers, and hyphens only
              </p>
            </div>

            <div>
              <label htmlFor="description" className="block text-sm font-medium text-slate-300 mb-2">
                Description (Optional)
              </label>
              <textarea
                id="description"
                value={description}
                onChange={(e) => setDescription(e.target.value)}
                placeholder="A brief description of your book..."
                rows={3}
                className="w-full px-4 py-2 bg-slate-800 border border-slate-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-teal-500 text-slate-200 placeholder-slate-500 resize-none"
              />
            </div>

            <div>
              <label className="block text-sm font-medium text-slate-300 mb-3">
                Visibility
              </label>
              <div className="space-y-3">
                <label className="flex items-center">
                  <input
                    type="radio"
                    value="private"
                    checked={isPrivate}
                    onChange={() => setIsPrivate(true)}
                    className="mr-3 accent-teal-500"
                  />
                  <div>
                    <div className="font-medium text-slate-200">Private</div>
                    <div className="text-sm text-slate-400">Only you can see this repository</div>
                  </div>
                </label>
                <label className="flex items-center">
                  <input
                    type="radio"
                    value="public"
                    checked={!isPrivate}
                    onChange={() => setIsPrivate(false)}
                    className="mr-3 accent-teal-500"
                  />
                  <div>
                    <div className="font-medium text-slate-200">Public</div>
                    <div className="text-sm text-slate-400">Anyone can see this repository</div>
                  </div>
                </label>
              </div>
            </div>

            {error && (
              <div className="bg-red-500/10 border border-red-500/20 text-red-400 px-4 py-3 rounded-lg">
                {error}
              </div>
            )}

            <div className="flex gap-4 pt-4">
              <button
                type="submit"
                disabled={!repoName.trim() || isCreating}
                className="flex-1 bg-teal-600 text-white py-2 px-4 rounded-lg hover:bg-teal-700 disabled:bg-slate-700 disabled:text-slate-400 disabled:cursor-not-allowed transition-colors"
              >
                {isCreating ? 'Creating Repository...' : 'Create Repository'}
              </button>
              <button
                type="button"
                onClick={() => router.back()}
                className="px-6 py-2 bg-slate-700 hover:bg-slate-600 text-slate-200 rounded-lg transition-colors"
              >
                Back
              </button>
            </div>
          </form>
        </div>
      </div>
    </div>
  )
}

export default function CreateRepoPage() {
  return (
    <Suspense fallback={
      <div className="min-h-screen flex items-center justify-center bg-slate-900">
        <div className="text-center">
          <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-teal-500 mx-auto mb-4"></div>
          <h2 className="text-xl font-semibold text-slate-200 mb-2">Loading...</h2>
          <p className="text-slate-400">Please wait...</p>
        </div>
      </div>
    }>
      <CreateRepoContent />
    </Suspense>
  )
}