From 40b8b5f112ef1ae14bcb1254673cb07c9b251436 Mon Sep 17 00:00:00 2001 From: NCanggoro Date: Tue, 3 Oct 2023 14:53:42 +0700 Subject: [PATCH] add create new locations and get indonesia regions --- src/constants/api.ts | 9 ++ src/pages/AddLocation/index.tsx | 243 ++++++++++++++++++++++++++++++++ src/pages/AddLocation/style.css | 33 +++++ src/pages/AddLocation/types.ts | 16 +++ src/pages/index.tsx | 4 + src/routes/index.tsx | 7 +- src/services/index.ts | 5 + src/services/locations.ts | 35 ++++- src/services/regions.ts | 46 ++++++ 9 files changed, 392 insertions(+), 6 deletions(-) create mode 100644 src/pages/AddLocation/index.tsx create mode 100644 src/pages/AddLocation/style.css create mode 100644 src/pages/AddLocation/types.ts create mode 100644 src/services/regions.ts diff --git a/src/constants/api.ts b/src/constants/api.ts index f81caef..79438b3 100644 --- a/src/constants/api.ts +++ b/src/constants/api.ts @@ -4,11 +4,16 @@ const SIGNUP_URI = `${BASE_URL}/user/signup` const LOGIN_URI = `${BASE_URL}/user/login` const LOGOUT_URI = `${BASE_URL}/user/logout` +const GET_REGIONS = `${BASE_URL}/regions`; +const GET_REGENCIES = `${BASE_URL}/region/regencies`; +const GET_PROVINCES = `${BASE_URL}/region/provinces`; + const GET_LIST_LOCATIONS_URI = `${BASE_URL}/locations`; const GET_LIST_TOP_LOCATIONS = `${BASE_URL}/locations/top-ratings` const GET_LIST_RECENT_LOCATIONS_RATING_URI = `${BASE_URL}/locations/recent` const GET_LOCATION_URI = `${BASE_URL}/location`; const GET_LOCATION_TAGS_URI = `${BASE_URL}/location/tags` +const POST_CREATE_LOCATION = GET_LIST_LOCATIONS_URI; const GET_IMAGES_BY_LOCATION_URI = `${BASE_URL}/images/location` @@ -20,9 +25,13 @@ export { SIGNUP_URI, LOGIN_URI, LOGOUT_URI, + GET_REGIONS, + GET_PROVINCES, + GET_REGENCIES, GET_LIST_RECENT_LOCATIONS_RATING_URI, GET_LIST_TOP_LOCATIONS, GET_LIST_LOCATIONS_URI, + POST_CREATE_LOCATION, GET_LOCATION_URI, GET_LOCATION_TAGS_URI, GET_IMAGES_BY_LOCATION_URI, diff --git a/src/pages/AddLocation/index.tsx b/src/pages/AddLocation/index.tsx new file mode 100644 index 0000000..0a8a617 --- /dev/null +++ b/src/pages/AddLocation/index.tsx @@ -0,0 +1,243 @@ +import { ChangeEvent, TargetedEvent, useEffect, useState } from "preact/compat"; +import { getListRecentLocationsRatingsService, getProvincesService, getRegenciesService } from "../../services"; +import { LocationInfo } from "../../domains/LocationInfo"; +import { DropdownInput } from "../../components"; +import { IndonesiaRegionsInfo, LocationType } from "../../types/common"; +import { Regency, emptyRegency } from "../../domains"; +import { Form } from './types'; +import { enumKeys } from "../../utils"; +import './style.css'; +import { createLocationService } from "../../services/locations"; +import { useSelector } from "react-redux"; +import { UserRootState } from "../../store/type"; + +function AddLocation() { + const [recentLocations, setRecentLocations] = useState>() + const [idnRegions, setIdnRegions] = useState({ + regencies: Array(), + }) + const [form, setForm] = useState
({ + name: '', + address: '', + google_maps_link: '', + location_type: LocationType.Beach, + regency: emptyRegency(), + thumbnails: [], + }) + + const user = useSelector((state: UserRootState) => state.auth) + + const [pageState, setPageState] = useState({ + regency_form_error: false, + }) + + async function getRecentLocations() { + try { + const locations = await getListRecentLocationsRatingsService(9) + setRecentLocations(locations.data) + // setIsLoading(false) + } catch (error) { + console.log(error) + } + } + + + async function getRegions() { + try { + const provinces = await getProvincesService(); + const regencies = await getRegenciesService(); + + setIdnRegions({ + provinces: provinces.data, + regencies: regencies.data + }) + + } catch (error) { + console.log(error) + } + } + + + async function onSubmitForm(e: TargetedEvent) { + e.preventDefault(); + + if(form.regency.regency_name === '') { + setPageState({ regency_form_error: true }) + return + } + + let tempThumbnailArr: Array = []; + let formData = new FormData(); + + form.thumbnails.forEach(x => { + tempThumbnailArr.push(x.file) + }) + + formData.append("address", form.address); + formData.append("location_type", form.location_type); + formData.append("name", form.name); + formData.append("regency_id", form.regency.id.toString()); + formData.append("submitted_by", user.id.toString()); + formData.append("google_maps_link", form.google_maps_link); + + for(let i = 0; i < tempThumbnailArr.length; i++) { + formData.append("thumbnail", tempThumbnailArr[i]) + } + + try { + const res = await createLocationService(formData) + alert("Location Added") + + } catch(error) { + console.log(error) + } + } + + function onChangeUploadImage(e: TargetedEvent) { + e.preventDefault() + let event = e.target as HTMLInputElement; + const files = Array.from(event.files as ArrayLike); + + const result = files.filter((x) => { + if (x.type === "image/jpg" || x.type === "image/png" || x.type === "image/jpeg") { + return true + } + return false + }).map(v => { + let ret = { + file: v, + url: URL.createObjectURL(v) + } + return ret + }) + + setForm({ ...form, thumbnails: result }) + } + + function onDeleteSelectedThumbnail(e: TargetedEvent, idx: number) { + e.preventDefault(); + + // remove image from FileList + const dt = new DataTransfer(); + const tempInput = document.getElementById('imageUpload')!; + const input = tempInput as HTMLInputElement; + const { files } = input; + + for (let i = 0; i < files!.length; i++) { + const file = files![i] + if (idx !== i) + dt.items.add(file) + } + + input.files = dt.files + + let thumbnails = form.thumbnails.filter((_, index) => index != idx); + setForm({ ...form, thumbnails: thumbnails }) + } + + function onChangeFormInput(e: ChangeEvent) { + let event = e.target as HTMLInputElement; + setForm({ ...form, [event.name]: event.value }) + } + + function onDeleteAllThumbnails(e: TargetedEvent) { + e.preventDefault(); + + const dt = new DataTransfer(); + const tempInput = document.getElementById('imageUpload'); + const input = tempInput as HTMLInputElement; + + dt.items.clear(); + input.files = dt.files + + setForm({ ...form, thumbnails: [] }) + } + + function onChangeRegencyDropdownInput(val: Regency) { + setPageState({ ...pageState, regency_form_error: false }) + setForm({ ...form, regency: val }) + } + + + useEffect(() => { + getRecentLocations() + getRegions() + }, []) + + return ( +
+
+
+

Add New Location

+
+ + Location Name * + + Address * + + + + Kota / Kabupaten * (regency mustn't be empty) + onChangeRegencyDropdownInput(val)} + labelPropsName={"regency_name"} + options={idnRegions.regencies!} + placeholder={''} + /> + Google Maps Link * + + Thumbnails + + +
+ {form.thumbnails.length > 0 && + form.thumbnails.map((x, idx) => ( + + )) + } + {form.thumbnails.length > 0 && + Delete All Thumbnails + } + + + + NOTE: LOCATION SUBMISSION MAY BE EDITED BY MODERATOR SO DON'T PUT STUPID ASS THUMBNAILS YOU 1 CENT DOORKNOB +
+
+
+

Recently added locations

+ {recentLocations?.map(x => ( +
+ + {x.name} + +
+ ))} +
+
+
+ ) +} + +export default AddLocation; \ No newline at end of file diff --git a/src/pages/AddLocation/style.css b/src/pages/AddLocation/style.css new file mode 100644 index 0000000..a0fed08 --- /dev/null +++ b/src/pages/AddLocation/style.css @@ -0,0 +1,33 @@ +.input-text { + border-radius: 7px; + min-width: 325px; + max-width: 450px; + width: 100%; + padding: 5px 10px; +} + +.inputfile { + width: 0.1px; + height: 0.1px; + opacity: 0; + overflow: hidden; + position: absolute; + z-index: -1; +} + +.inputfile + label { + font-size: 14px; + color: white; + display: inline-block; + padding: 5px; + background-color: #555555; + border-radius: 10px; +} + +.inputfile + label:hover { + background-color: #202225; +} + +.inputfile + label { + cursor: pointer; /* "hand" cursor */ +} \ No newline at end of file diff --git a/src/pages/AddLocation/types.ts b/src/pages/AddLocation/types.ts new file mode 100644 index 0000000..91f787c --- /dev/null +++ b/src/pages/AddLocation/types.ts @@ -0,0 +1,16 @@ +import { LocationType } from "src/types/common" +import { Regency } from "../../domains" + +export interface Thumbnail { + file: File, + url: string +} + +export interface Form { + name: string, + address: string, + regency: Regency, + location_type: LocationType, + google_maps_link: string, + thumbnails: Array +} diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 8be217e..4822b49 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -6,6 +6,8 @@ import NewsEvent from "./NewsEvents"; import LocationDetail from "./LocationDetail"; import Login from './Login'; import NotFound from "./NotFound"; +import AddLocation from "./AddLocation"; +import Submissions from "./Submissions"; export { Login, @@ -15,7 +17,9 @@ export { NotFound, BestLocation, + AddLocation, LocationDetail, + Submissions, Discovery, Story, diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 8984274..af291de 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -5,7 +5,7 @@ import { LocationDetail, NewsEvent, Story, - Login + AddLocation } from '../pages'; const routes = [ @@ -38,6 +38,11 @@ const routes = [ path: "/location/:id", name: "LocationDetail", element: + }, + { + path: "/add-location", + name: "AddLocation", + element: } ] diff --git a/src/services/index.ts b/src/services/index.ts index a7112c4..d4d3518 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -8,12 +8,17 @@ import { import { getImagesByLocationService } from "./images" import { createAccountService, loginService, logoutService } from "./auth"; import { postReviewLocation, getCurrentUserLocationReviewService } from "./review"; +import { getRegionsService, getProvincesService, getRegenciesService} from "./regions"; export { createAccountService, loginService, logoutService, + getRegionsService, + getProvincesService, + getRegenciesService, + getListLocationsService, getListRecentLocationsRatingsService, getListTopLocationsService, diff --git a/src/services/locations.ts b/src/services/locations.ts index 3f700b4..266da77 100644 --- a/src/services/locations.ts +++ b/src/services/locations.ts @@ -1,18 +1,27 @@ -import { GET_LIST_LOCATIONS_URI, GET_LIST_RECENT_LOCATIONS_RATING_URI, GET_LIST_TOP_LOCATIONS, GET_LOCATION_TAGS_URI, GET_LOCATION_URI } from "../constants/api"; +import { GetRequestPagination, IHttpResponse } from "../types/common"; +import { + GET_LIST_LOCATIONS_URI, + GET_LIST_RECENT_LOCATIONS_RATING_URI, + GET_LIST_TOP_LOCATIONS, + GET_LOCATION_TAGS_URI, + GET_LOCATION_URI, + POST_CREATE_LOCATION +} from "../constants/api"; import { client } from "./config"; import statusCode from "./status-code"; +import { AxiosError } from "axios"; const initialState: any = { data: null, error: null } -interface getListLocationsArg extends GetRequestPagination { +interface GetListLocationsArg extends GetRequestPagination { order_by?: number, region_type?: number } -async function getListLocationsService({ page, page_size }: getListLocationsArg) { +async function getListLocationsService({ page, page_size }: GetListLocationsArg) { const newState = { ...initialState }; const url = `${GET_LIST_LOCATIONS_URI}?page=${page}&page_size=${page_size}` try { @@ -48,7 +57,7 @@ async function getListRecentLocationsRatingsService(page_size: Number) { } } -async function getListTopLocationsService({ page, page_size, order_by, region_type }: getListLocationsArg) { +async function getListTopLocationsService({ page, page_size, order_by, region_type }: GetListLocationsArg) { const newState = { ...initialState }; const url = `${GET_LIST_TOP_LOCATIONS}?page=${page}&page_size=${page_size}&order_by=${order_by}®ion_type=${region_type}` try { @@ -102,10 +111,26 @@ async function getLocationTagsService(id: Number) { } } +async function createLocationService(data: FormData): Promise { + const newState: IHttpResponse = { data: null, error: null}; + try { + const response = await client({ method: 'POST', url: POST_CREATE_LOCATION, data: data}) + newState.data = response.data; + newState.status = response.status + return newState; + } catch (error) { + let err = error as AxiosError; + newState.error = err; + newState.status = err.status; + return newState; + } +} + export { getListLocationsService, getListRecentLocationsRatingsService, getListTopLocationsService, getLocationTagsService, - getLocationService + getLocationService, + createLocationService, } \ No newline at end of file diff --git a/src/services/regions.ts b/src/services/regions.ts new file mode 100644 index 0000000..394bdb6 --- /dev/null +++ b/src/services/regions.ts @@ -0,0 +1,46 @@ +import { client } from "./config"; +import { GET_PROVINCES, GET_REGENCIES, GET_REGIONS } from "../constants/api"; +import { IHttpResponse } from "src/types/common"; + +async function getRegionsService(): Promise { + const newState: IHttpResponse = {data: null, error: null} + try { + const response = await client({ method: 'GET', url: GET_REGIONS}) + newState.data = response.data; + return newState + } catch(err) { + newState.error = err + throw (newState) + } +} + +async function getProvincesService(): Promise { + const newState: IHttpResponse = { data: null, error: null} + try { + const response = await client({ method: 'GET', url: GET_PROVINCES}) + newState.data = response.data; + return newState + } catch(err) { + newState.error = err + throw (newState) + } +} + +async function getRegenciesService(): Promise { + const newState: IHttpResponse = { data: null, error: null}; + try { + const response = await client({ method: 'GET', url: GET_REGENCIES}) + newState.data = response.data; + newState.status = response.status + return newState + } catch(err) { + newState.error = err + throw (newState) + } +} + +export { + getRegionsService, + getProvincesService, + getRegenciesService, +} \ No newline at end of file