Initial commit: WordPress wp-content (themes, plugins, languages)
- Theme: momentry (custom theme with REST API routes) - Plugins: code-snippets (contains all API proxies) - Languages: zh_TW translations - Excludes: cache, backups, uploads, logs
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
export { useDragAndDrop } from './useDragAndDrop'
|
||||
export { useFileSelection } from './useFileSelection'
|
||||
export { useSnippetSelection } from './useSnippetSelection'
|
||||
export { useImportWorkflow } from './useImportWorkflow'
|
||||
@@ -0,0 +1,36 @@
|
||||
import { useState } from 'react'
|
||||
|
||||
interface UseDragAndDropProps {
|
||||
onFilesDrop: (files: FileList) => void
|
||||
}
|
||||
|
||||
export const useDragAndDrop = ({ onFilesDrop }: UseDragAndDropProps) => {
|
||||
const [dragOver, setDragOver] = useState(false)
|
||||
|
||||
const handleDragOver = (e: React.DragEvent) => {
|
||||
e.preventDefault()
|
||||
setDragOver(true)
|
||||
}
|
||||
|
||||
const handleDragLeave = (e: React.DragEvent) => {
|
||||
e.preventDefault()
|
||||
setDragOver(false)
|
||||
}
|
||||
|
||||
const handleDrop = (e: React.DragEvent) => {
|
||||
e.preventDefault()
|
||||
setDragOver(false)
|
||||
|
||||
const files = e.dataTransfer.files
|
||||
if (files.length > 0) {
|
||||
onFilesDrop(files)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
dragOver,
|
||||
handleDragOver,
|
||||
handleDragLeave,
|
||||
handleDrop
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { useState, useRef } from 'react'
|
||||
import { removeFileFromList } from '../utils/fileUtils'
|
||||
|
||||
export const useFileSelection = () => {
|
||||
const [selectedFiles, setSelectedFiles] = useState<FileList | null>(null)
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
const handleFileSelect = (files: FileList | null) => {
|
||||
setSelectedFiles(files)
|
||||
}
|
||||
|
||||
const removeFile = (index: number) => {
|
||||
if (!selectedFiles) return
|
||||
|
||||
const newFiles = removeFileFromList(selectedFiles, index)
|
||||
setSelectedFiles(newFiles)
|
||||
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.files = newFiles
|
||||
}
|
||||
}
|
||||
|
||||
const clearFiles = () => {
|
||||
setSelectedFiles(null)
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
const triggerFileInput = () => {
|
||||
fileInputRef.current?.click()
|
||||
}
|
||||
|
||||
return {
|
||||
selectedFiles,
|
||||
fileInputRef,
|
||||
handleFileSelect,
|
||||
removeFile,
|
||||
clearFiles,
|
||||
triggerFileInput
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import { useState } from 'react'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
import { useFileUploadAPI, type ImportableSnippet } from '../../../../hooks/useFileUploadAPI'
|
||||
import { isNetworkAdmin } from '../../../../utils/screen'
|
||||
|
||||
type DuplicateAction = 'ignore' | 'replace' | 'skip'
|
||||
|
||||
interface UploadResult {
|
||||
success: boolean
|
||||
message: string
|
||||
imported?: number
|
||||
warnings?: string[]
|
||||
}
|
||||
|
||||
export const useImportWorkflow = () => {
|
||||
const [isUploading, setIsUploading] = useState(false)
|
||||
const [isImporting, setIsImporting] = useState(false)
|
||||
const [availableSnippets, setAvailableSnippets] = useState<ImportableSnippet[]>([])
|
||||
const [uploadResult, setUploadResult] = useState<UploadResult | null>(null)
|
||||
|
||||
const fileUploadAPI = useFileUploadAPI()
|
||||
|
||||
const parseFiles = async (files: FileList): Promise<boolean> => {
|
||||
if (!files || files.length === 0) {
|
||||
alert(__('Please select files to upload.', 'code-snippets'))
|
||||
return false
|
||||
}
|
||||
|
||||
setIsUploading(true)
|
||||
setUploadResult(null)
|
||||
|
||||
try {
|
||||
const response = await fileUploadAPI.parseFiles({ files })
|
||||
|
||||
setAvailableSnippets(response.data.snippets)
|
||||
|
||||
if (response.data.warnings && response.data.warnings.length > 0) {
|
||||
setUploadResult({
|
||||
success: true,
|
||||
message: response.data.message,
|
||||
warnings: response.data.warnings
|
||||
})
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
} catch (error) {
|
||||
console.error('Parse error:', error)
|
||||
setUploadResult({
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : __('An unknown error occurred.', 'code-snippets')
|
||||
})
|
||||
return false
|
||||
} finally {
|
||||
setIsUploading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const importSnippets = async (
|
||||
snippetsToImport: ImportableSnippet[],
|
||||
duplicateAction: DuplicateAction
|
||||
): Promise<boolean> => {
|
||||
if (snippetsToImport.length === 0) {
|
||||
alert(__('Please select snippets to import.', 'code-snippets'))
|
||||
return false
|
||||
}
|
||||
|
||||
setIsImporting(true)
|
||||
setUploadResult(null)
|
||||
|
||||
try {
|
||||
const response = await fileUploadAPI.importSnippets({
|
||||
snippets: snippetsToImport,
|
||||
duplicate_action: duplicateAction,
|
||||
network: isNetworkAdmin()
|
||||
})
|
||||
|
||||
setUploadResult({
|
||||
success: true,
|
||||
message: response.data.message,
|
||||
imported: response.data.imported
|
||||
})
|
||||
|
||||
return true
|
||||
|
||||
} catch (error) {
|
||||
console.error('Import error:', error)
|
||||
setUploadResult({
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : __('An unknown error occurred.', 'code-snippets')
|
||||
})
|
||||
return false
|
||||
} finally {
|
||||
setIsImporting(false)
|
||||
}
|
||||
}
|
||||
|
||||
const resetWorkflow = () => {
|
||||
setAvailableSnippets([])
|
||||
setUploadResult(null)
|
||||
}
|
||||
|
||||
const clearUploadResult = () => {
|
||||
setUploadResult(null)
|
||||
}
|
||||
|
||||
return {
|
||||
isUploading,
|
||||
isImporting,
|
||||
availableSnippets,
|
||||
uploadResult,
|
||||
parseFiles,
|
||||
importSnippets,
|
||||
resetWorkflow,
|
||||
clearUploadResult
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { useState } from 'react'
|
||||
import type { ImportableSnippet } from '../../../../hooks/useFileUploadAPI'
|
||||
|
||||
export const useSnippetSelection = (availableSnippets: ImportableSnippet[]) => {
|
||||
const [selectedSnippets, setSelectedSnippets] = useState<Set<number | string>>(new Set())
|
||||
|
||||
const handleSnippetToggle = (snippetId: number | string) => {
|
||||
const newSelected = new Set(selectedSnippets)
|
||||
if (newSelected.has(snippetId)) {
|
||||
newSelected.delete(snippetId)
|
||||
} else {
|
||||
newSelected.add(snippetId)
|
||||
}
|
||||
setSelectedSnippets(newSelected)
|
||||
}
|
||||
|
||||
const handleSelectAll = () => {
|
||||
if (selectedSnippets.size === availableSnippets.length) {
|
||||
setSelectedSnippets(new Set())
|
||||
} else {
|
||||
setSelectedSnippets(new Set(availableSnippets.map(snippet => snippet.table_data.id)))
|
||||
}
|
||||
}
|
||||
|
||||
const clearSelection = () => {
|
||||
setSelectedSnippets(new Set())
|
||||
}
|
||||
|
||||
const getSelectedSnippets = () => {
|
||||
return availableSnippets.filter(snippet =>
|
||||
selectedSnippets.has(snippet.table_data.id)
|
||||
)
|
||||
}
|
||||
|
||||
const isAllSelected = selectedSnippets.size === availableSnippets.length && availableSnippets.length > 0
|
||||
|
||||
return {
|
||||
selectedSnippets,
|
||||
handleSnippetToggle,
|
||||
handleSelectAll,
|
||||
clearSelection,
|
||||
getSelectedSnippets,
|
||||
isAllSelected
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user