feat: add images input components to list-project, connect with api

This commit is contained in:
MTG2000
2022-09-15 15:27:27 +03:00
parent 9710893a42
commit 318367de2d
7 changed files with 80 additions and 37 deletions

View File

@@ -34,7 +34,7 @@ export default function CoverImageInput(props: Props) {
wrapperClass='h-full'
render={({ img, isUploading, isDraggingOnWindow }) =>
<div className="w-full h-full group relative ">
{!img && <div className='w-full h-full flex flex-col justify-center items-center bg-gray-500 outline outline-2 outline-gray-200'>
{!img && <div className={`w-full h-full flex flex-col justify-center items-center bg-gray-500 outline outline-2 outline-gray-200 ${props.rounded ?? 'rounded-12'}`}>
<p className="text-center text-gray-100 text-body1 md:text-h1 mb-8"><FaImage /></p>
<div className={`text-gray-100 text-center text-body4`}>
Drop a <span className="font-bold">COVER IMAGE</span> here or <br /> <span className="text-blue-300 underline">Click to browse</span>
@@ -56,7 +56,7 @@ export default function CoverImageInput(props: Props) {
</>}
{isUploading &&
<div
className="absolute inset-0 bg-gray-400 bg-opacity-60 flex flex-col justify-center items-center text-white font-bold transition-transform"
className={`absolute inset-0 bg-gray-400 ${props.rounded ?? 'rounded-12'} bg-opacity-60 flex flex-col justify-center items-center text-white font-bold transition-transform`}
>
<RotatingLines
strokeColor="#fff"

View File

@@ -1,13 +1,14 @@
import React from 'react'
import { ComponentStory, ComponentMeta } from '@storybook/react';
import ScreenshotsInput, { ScreenshotType } from './ScreenshotsInput';
import ScreenshotsInput from './ScreenshotsInput';
import { WrapForm, WrapFormController } from 'src/utils/storybook/decorators';
import { ImageInput } from 'src/graphql';
export default {
title: 'Shared/Inputs/Files Inputs/Screenshots',
component: ScreenshotsInput,
decorators: [
WrapFormController<{ screenshots: Array<ScreenshotType> }>({
WrapFormController<{ screenshots: Array<ImageInput> }>({
logValues: true,
name: "screenshots",
defaultValues: {
@@ -29,7 +30,7 @@ Empty.args = {
export const WithValues = Template.bind({});
WithValues.decorators = [
WrapFormController<{ screenshots: Array<ScreenshotType> }>({
WrapFormController<{ screenshots: Array<ImageInput> }>({
logValues: true,
name: "screenshots",
defaultValues: {
@@ -51,7 +52,7 @@ WithValues.args = {
export const Full = Template.bind({});
Full.decorators = [
WrapFormController<{ screenshots: Array<ScreenshotType> }>({
WrapFormController<{ screenshots: Array<ImageInput> }>({
logValues: true,
name: "screenshots",
defaultValues: {

View File

@@ -11,6 +11,8 @@ import { getMockSenderEnhancer } from "@rpldy/mock-sender";
import ScreenshotThumbnail from "./ScreenshotThumbnail";
import { FiCamera } from "react-icons/fi";
import { Control, Path, useController } from "react-hook-form";
import { ImageInput } from "src/graphql";
import { fetchUploadImageUrl } from "src/api/uploading";
@@ -20,15 +22,10 @@ const mockSenderEnhancer = getMockSenderEnhancer({
const MAX_UPLOAD_COUNT = 4 as const;
export interface ScreenshotType {
id: string,
name: string,
url: string;
}
interface Props {
value: ScreenshotType[],
onChange: (new_value: ScreenshotType[]) => void
value: ImageInput[],
onChange: (new_value: ImageInput[]) => void
}
@@ -49,7 +46,6 @@ export default function ScreenshotsInput(props: Props) {
multiple={true}
inputFieldName='file'
grouped={false}
enhancer={mockSenderEnhancer}
listeners={{
[UPLOADER_EVENTS.BATCH_ADD]: (batch) => {
setUploadingCount(v => v + batch.items.length)
@@ -96,17 +92,18 @@ const DropZone = forwardRef<any, any>((props, ref) => {
useRequestPreSend(async (data) => {
const filename = data.items?.[0].file.name ?? ''
// const url = await fetchUploadUrl({ filename });
const res = await fetchUploadImageUrl({ filename });
return {
options: {
destination: {
url: "URL"
}
url: res.uploadURL
},
}
}
})
const onZoneClick = useCallback(

View File

@@ -71,11 +71,11 @@ const schema: yup.SchemaOf<IListProjectForm> = yup.object({
github: yup.string().url().ensure(),
category_id: yup.number().required("Please choose a category"),
capabilities: yup.array().of(yup.string().required()).default([]),
screenshots: yup.array().of(yup.string().required()).default([]),
screenshots: yup.array().of(imageSchema.required()).default([]),
members: yup.array().of(yup.object() as any).default([]),
recruit_roles: yup.array().of(yup.object() as any).default([]),
recruit_roles: yup.array().of(yup.number().required()).default([]),
launch_status: yup.mixed().oneOf(['wip', 'launched']).default('wip'),
tournaments: yup.array().default([])
tournaments: yup.array().of(yup.number().required()).default([])
}).required();
export default function FormContainer(props: PropsWithChildren<Props>) {

View File

@@ -5,6 +5,9 @@ import { FiCamera, FiGithub, FiTwitter } from "react-icons/fi";
import CategoriesInput from "../CategoriesInput/CategoriesInput";
import CapabilitiesInput from "../CapabilitiesInput/CapabilitiesInput";
import { IListProjectForm } from "../FormContainer/FormContainer";
import AvatarInput from "src/Components/Inputs/FilesInputs/AvatarInput/AvatarInput";
import CoverImageInput from "src/Components/Inputs/FilesInputs/CoverImageInput/CoverImageInput";
import ScreenshotsInput from "src/Components/Inputs/FilesInputs/ScreenshotsInput/ScreenshotsInput";
interface Props { }
@@ -21,15 +24,28 @@ export default function ProjectDetailsTab(props: Props) {
<div className="md:col-span-2 flex flex-col gap-24">
<Card className="" defaultPadding={false}>
<div className="bg-gray-600 relative h-[160px] rounded-t-12 md:rounded-t-16">
<Controller
control={control}
name="cover_image"
render={({ field: { onChange, value, onBlur, ref } }) => <CoverImageInput
value={value}
rounded='rounded-t-12 md:rounded-t-16'
onChange={e => {
onChange(e)
}}
// uploadText='Add a cover image'
/>
}
/>
<div className="absolute left-24 bottom-0 translate-y-1/2">
{/* <Avatar src={data.avatar} width={120} /> */}
<div
className="rounded-full w-[120px] aspect-square border-2 border-gray-200 bg-white flex flex-col gap-8 items-center justify-center"
role={'button'}
>
<FiCamera className="text-gray-400 text-h2" />
<span className="text-gray-400 text-body6">Add image</span>
</div>
<Controller
control={control}
name="thumbnail_image"
render={({ field: { onChange, value } }) => (
<AvatarInput value={value} onChange={onChange} width={120} />
)}
/>
</div>
</div>
<div className="p-16 md:p-24 mt-64">
@@ -184,6 +200,26 @@ export default function ProjectDetailsTab(props: Props) {
{errors.capabilities && <p className='input-error'>{errors.capabilities?.message}</p>}
</div>
</Card>
<Card>
<h2 className="text-body2 font-bolder">📷 Screenshots</h2>
<p className="text-body4 font-light text-gray-600 mt-8">Choose up to 4 images from your project</p>
<div className="mt-24">
<Controller
control={control}
name="screenshots"
render={({ field: { onChange, value, onBlur, ref } }) => <ScreenshotsInput
value={value}
onChange={e => {
onChange(e)
}}
/>
}
/>
{errors.capabilities && <p className='input-error'>{errors.capabilities?.message}</p>}
</div>
</Card>
</div>
)
}

View File

@@ -9,31 +9,40 @@ import { tabs } from '../../ListProjectPage'
import { NotificationsService } from 'src/services'
import { useAppDispatch } from 'src/utils/hooks';
import { openModal } from 'src/redux/features/modals.slice';
import { useCreateProjectMutation, useUpdateProjectMutation } from 'src/graphql'
interface Props {
currentTab: keyof typeof tabs
onNext: () => void
onBackToFirstPage: () => void
}
export default function SaveChangesCard(props: Props) {
const { handleSubmit, formState: { errors, isDirty, }, reset, getValues, watch } = useFormContext<IListProjectForm>();
const navigate = useNavigate();
const dispatch = useAppDispatch();
const [isLoading, setIsLoading] = useState(false);
const isUpdating = useMemo(() => !!getValues('id'), [getValues]);
const [update, updatingStatus] = useUpdateProjectMutation();
const [create, creatingStatus] = useCreateProjectMutation()
const [img, name, tagline] = watch(['thumbnail_image', 'title', 'tagline'])
const [img, name, tagline] = watch(['thumbnail_image', 'title', 'tagline',])
const clickCancel = () => {
if (window.confirm('You might lose some unsaved changes. Are you sure you want to continue?'))
reset();
}
const clickSubmit = handleSubmit<IListProjectForm>(data => {
const clickSubmit = handleSubmit<IListProjectForm>(async data => {
try {
await (isUpdating ? update({ variables: { input: data } }) : create({ variables: { input: data } }))
} catch (error) {
NotificationsService.error("A network error happened...");
return;
}
if (isUpdating)
NotificationsService.success("Saved changes successfully")
else {
@@ -48,10 +57,9 @@ export default function SaveChangesCard(props: Props) {
}
}))
}
console.log(data);
}, () => {
}, (errors) => {
NotificationsService.error("Please fill all the required fields");
navigate(tabs['project-details'].path)
props.onBackToFirstPage()
})
@@ -91,7 +99,7 @@ export default function SaveChangesCard(props: Props) {
>
List your product
</Button>
}, [clickSubmit, isDirty, isLoading, isUpdating, props.currentTab])
}, [clickSubmit, isDirty, isLoading, isUpdating, props.currentTab, props.onNext])
return (

View File

@@ -117,6 +117,7 @@ export default function ListProjectPage() {
if (curTab === 'project-details') setCurTab(tabs['team'].path)
else if (curTab === 'team') setCurTab(tabs['extras'].path)
}}
onBackToFirstPage={() => setCurTab(tabs["project-details"].path)}
/>
</div>
</div>