feat: showcase roadmap (#7791)
* wip * wip * fix: status issue * feat: update UI * wip * wip: showcase status * wip: showcase listing * feat: update showcase status * chore: update roadmap content json (#7738) Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com> * Fix issue in sticky top ad * Add preloading of ad image * feat(backend): update unit testing node resources (#7743) * feat: container orchestration * Update container-orchestration@Yq8kVoRf20aL_o4VZU5--.md Simplified content and added working links to resources for better clarity and learning. * Update container-orchestration@Yq8kVoRf20aL_o4VZU5--.md Replace content and added working links to resources for better clarity and learning. * Update container-orchestration@Yq8kVoRf20aL_o4VZU5--.md * Update src/data/roadmaps/devops/content/container-orchestration@Yq8kVoRf20aL_o4VZU5--.md --------- Co-authored-by: Arik Chakma <arikchangma@gmail.com> * docs: fix typos and improve grammar in documentation (#7747) Corrects typos and grammatical errors in various markdown files to enhance clarity and readability. * feat: add PearAI code editor Added PearAI to the list of AI Code Editors (An Open Source Option for developers!) * chore: update roadmap content json (#7751) Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com> * feat: center of mass explain video (#7754) video addition explaining COM better * Ad new changelog entry * Update C# link to correct URL (#7757) * Add engineering manager roadmap * chore: update roadmap content json (#7758) Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com> * Update frontend FAQs (#7764) Tweaked the first two Qs * Update DevOps skills (#7763) Added internal refs * Add ref to DevOps roadmap in guide (#7762) Added roadmap ref. * Add engineering manager roadmap * Update engineering manager roadmap content * Update engineering manager roadmap * Add content to engineering manager roadmap * chore: update roadmap content json (#7768) Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com> * fix: postgresql link (#7766) * fix(typo): comma todo-list-api.md (#7772) * Add new link of Redis in FullStack (#7771) * chore: update roadmap content json (#7778) Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com> * Add content to vue.js performance (#7777) * Update performance@f7N4pAp_jBlT8_8owAcbG.md * Update src/data/roadmaps/vue/content/performance@f7N4pAp_jBlT8_8owAcbG.md --------- Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com> * Update resources for Authentication (#7745) * Update authentication-vs-authorization@WG7DdsxESm31VcLFfkVTz.md replaced a wrong article with one about biometrics * Update understand-common-exploit-frameworks@Lg7mz4zeCToEzZBFxYuaU.md link redirects to a Thai gambling game site * Add resource for rest-assured (#7737) ## Content I’ve added a beginner-friendly article, A Guide to REST-assured, from Baeldung to the REST Assured section. If there’s anything that doesn’t meet the format, please feel free to comment. Thanks😊. ## Issue Fixed #7736 * Add UX design resource (#7710) * Update conceptual-design@r6D07cN0Mg4YXsiRSrl1_.md I have added an article by Dan Nessler on How to apply a design thinking, HCD, UX or any creative process from scratch which is a how-to article aims at providing designers, creative thinkers or even project managers with a tool to set up, frame, organise, structure, run or manage design challenges, and projects: The Double Diamond revamped. * Update conceptual-design@r6D07cN0Mg4YXsiRSrl1_.md I have added an article by Dan Nessler on How to apply a design thinking, HCD, UX or any creative process from scratch which is a how-to article aims at providing designers, creative thinkers or even project managers with a tool to set up, frame, organise, structure, run or manage design challenges, and projects: The Double Diamond revamped. * Add user personas resource to UX design (#7709) Added more resources from IxD Foundation and NN group. Co-authored-by: Shivam Kumar <85393390+TinyTijil@users.noreply.github.com> * Add linked in content (#7695) * Update linkedin@6UR59TigEZ0NaixbaUIqn.md * Update src/data/roadmaps/devrel/content/linkedin@6UR59TigEZ0NaixbaUIqn.md --------- Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com> * Added article on AuthN vs AuthZ (#7694) Added a guide on the difference between authentication and authorization, since these terms are often confused. * Add a video to the Decentralization section (#7692) * adding a video to the Decentralization section * adding a video to the Decentralization section * Fixes typo in 104-proc-priorities.md (#7684) Old: renice +5 New: renice -5 From my research, after reading the topic in the Linux roadmap, it didnt make sense that increasing the priority of a process was made by +5, the topic said that a negative number makes the priority higher, so do many articles on the internet. * Add bastion host and file integrity checker idea * Add pomodoro timer project idea * Add project idea for quiz app * chore: update roadmap content json (#7785) Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com> * fix typo in dockerhub alternatives (#7780) Co-authored-by: Fabio Stabile <fabio.stabile@mia-platform.eu> * Add content to engineering manager roadmap (#7779) * Update system-design-and-architecture@iX4HPgoiEbc_gze1A01n4.md * Update src/data/roadmaps/engineering-manager/content/system-design-and-architecture@iX4HPgoiEbc_gze1A01n4.md * Update src/data/roadmaps/engineering-manager/content/system-design-and-architecture@iX4HPgoiEbc_gze1A01n4.md --------- Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com> * Add project idea for stories feature * Add weather app project idea * Update project ideas * Add engineering manager roadmap content * Update engineering manager roadmap content * Add DevOps best practices guide * Add AI Engineer introduction video (#7788) * Added Introduction Video * Changed formatting * Update src/data/roadmaps/ai-engineer/content/introduction@_hYN0gEi9BL24nptEtXWU.md --------- Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com> * chore: update roadmap content json (#7789) Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com> * Add devops automation tools * Add featuring functionality --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com> Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com> Co-authored-by: Rogério Ferreira de Souza <rogeriofrsouza@gmail.com> Co-authored-by: Jawher Kl <kalleljawher4@gmail.com> Co-authored-by: garyellow <gary20011110@gmail.com> Co-authored-by: Nang <nathanang2000@gmail.com> Co-authored-by: FormerlyWD <156501761+FormerlyWD@users.noreply.github.com> Co-authored-by: dudi vaichledere <117650526+dudi-w@users.noreply.github.com> Co-authored-by: Ed Lan <165309301+Edlan01@users.noreply.github.com> Co-authored-by: elias_sisay <87943132+eliassisay@users.noreply.github.com> Co-authored-by: feelsgoodfrog <gudrb963@gmail.com> Co-authored-by: Gustavo Martins Pereira <gustavo.martins.pereira.main@gmail.com> Co-authored-by: Maksymilian <maxsapa@gmail.com> Co-authored-by: b4haa7 <69992780+88BahaaAdel88@users.noreply.github.com> Co-authored-by: Wick Dynex <1328032567@qq.com> Co-authored-by: Shivam Kumar <85393390+kshivam14@users.noreply.github.com> Co-authored-by: Shivam Kumar <85393390+TinyTijil@users.noreply.github.com> Co-authored-by: Yanbo Wang <yanbotravelaroundworld@gmail.com> Co-authored-by: Lisa Dziuba <lisa@flawlessapp.io> Co-authored-by: Karamoko Israël Abdelaziz Axel <72276211+karamokoisrael@users.noreply.github.com> Co-authored-by: duds <xaviduds@gmail.com> Co-authored-by: Fabio Stabile <93452841+fabioS24@users.noreply.github.com> Co-authored-by: Fabio Stabile <fabio.stabile@mia-platform.eu> Co-authored-by: Naresh Thakur <122244033+thinklikeacto@users.noreply.github.com> Co-authored-by: Gustaf <79180496+GGyll@users.noreply.github.com>fix/error
parent
ee95280452
commit
43849e758e
13 changed files with 610 additions and 168 deletions
@ -0,0 +1,88 @@ |
|||||||
|
import { EyeIcon, FlagIcon, FrownIcon, SmileIcon } from 'lucide-react'; |
||||||
|
import { cn } from '../../../lib/classname'; |
||||||
|
import type { GetRoadmapResponse } from '../CustomRoadmap'; |
||||||
|
import { useState } from 'react'; |
||||||
|
import { SubmitShowcaseWarning } from './SubmitShowcaseWarning'; |
||||||
|
|
||||||
|
type ShowcaseAlertProps = { |
||||||
|
currentRoadmap: GetRoadmapResponse; |
||||||
|
}; |
||||||
|
|
||||||
|
export function ShowcaseAlert(props: ShowcaseAlertProps) { |
||||||
|
const { currentRoadmap } = props; |
||||||
|
|
||||||
|
const [showRejectedReason, setShowRejectedReason] = useState(false); |
||||||
|
|
||||||
|
const { showcaseStatus } = currentRoadmap; |
||||||
|
if (!showcaseStatus) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
const showcaseStatusMap = { |
||||||
|
submitted: { |
||||||
|
icon: EyeIcon, |
||||||
|
label: |
||||||
|
'We are currently reviewing your roadmap, please wait for our response.', |
||||||
|
className: 'bg-blue-100 text-blue-600 border-blue-200', |
||||||
|
}, |
||||||
|
approved: { |
||||||
|
icon: SmileIcon, |
||||||
|
label: 'Hooray! Your roadmap is now visible on the community page.', |
||||||
|
className: 'text-green-600 bg-green-100 border-green-300', |
||||||
|
}, |
||||||
|
rejected: { |
||||||
|
icon: FrownIcon, |
||||||
|
label: 'Sorry, we are unable to feature your roadmap at this time.', |
||||||
|
className: 'text-red-600 bg-red-100 border-red-300', |
||||||
|
}, |
||||||
|
rejected_with_reason: { |
||||||
|
icon: FlagIcon, |
||||||
|
label: ( |
||||||
|
<> |
||||||
|
Your roadmap could not be featured at this time{' '} |
||||||
|
<button |
||||||
|
className="font-medium underline underline-offset-2 hover:text-red-800" |
||||||
|
onClick={() => { |
||||||
|
setShowRejectedReason(true); |
||||||
|
}} |
||||||
|
> |
||||||
|
click here to see why |
||||||
|
</button> |
||||||
|
</> |
||||||
|
), |
||||||
|
className: 'text-red-800 bg-red-200 border-red-200', |
||||||
|
}, |
||||||
|
}; |
||||||
|
const showcaseStatusDetails = showcaseStatusMap[showcaseStatus]; |
||||||
|
if (!showcaseStatusDetails) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
const { icon: Icon, label, className } = showcaseStatusDetails; |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
{showRejectedReason && ( |
||||||
|
<SubmitShowcaseWarning |
||||||
|
onClose={() => { |
||||||
|
setShowRejectedReason(false); |
||||||
|
}} |
||||||
|
/> |
||||||
|
)} |
||||||
|
|
||||||
|
<div |
||||||
|
className={cn( |
||||||
|
'z-10 border-b -mb-4', |
||||||
|
showcaseStatusDetails.className, |
||||||
|
)} |
||||||
|
> |
||||||
|
<div className="container relative flex items-center justify-center py-2 text-sm"> |
||||||
|
<div className={cn('flex items-center gap-2', className)}> |
||||||
|
<Icon className="h-4 w-4 shrink-0 stroke-[2.5]" /> |
||||||
|
<div>{label}</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
import { useState } from 'react'; |
||||||
|
import { SubmitShowcaseWarning } from './SubmitShowcaseWarning'; |
||||||
|
import type { GetRoadmapResponse } from '../CustomRoadmap'; |
||||||
|
import { SendIcon } from 'lucide-react'; |
||||||
|
|
||||||
|
type ShowcaseStatusProps = { |
||||||
|
currentRoadmap: GetRoadmapResponse; |
||||||
|
}; |
||||||
|
|
||||||
|
export function ShowcaseStatus(props: ShowcaseStatusProps) { |
||||||
|
const { currentRoadmap } = props; |
||||||
|
|
||||||
|
const { showcaseStatus } = currentRoadmap; |
||||||
|
const [showSubmitWarning, setShowSubmitWarning] = useState(false); |
||||||
|
|
||||||
|
if (!currentRoadmap || showcaseStatus) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
{showSubmitWarning && ( |
||||||
|
<SubmitShowcaseWarning |
||||||
|
onClose={() => { |
||||||
|
setShowSubmitWarning(false); |
||||||
|
}} |
||||||
|
/> |
||||||
|
)} |
||||||
|
|
||||||
|
<button |
||||||
|
className="inline-flex items-center justify-center rounded-md border border-gray-300 bg-white py-1.5 pl-2 pr-2 text-xs font-medium text-black hover:border-gray-300 hover:bg-gray-300 sm:pl-1.5 sm:pr-3 sm:text-sm" |
||||||
|
onClick={() => { |
||||||
|
setShowSubmitWarning(true); |
||||||
|
}} |
||||||
|
disabled={!!showcaseStatus} |
||||||
|
> |
||||||
|
<SendIcon className="mr-0 h-4 w-4 stroke-[2.5] sm:mr-1.5" /> |
||||||
|
<span className="hidden sm:inline">Apply to be Featured</span> |
||||||
|
</button> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,122 @@ |
|||||||
|
import { useMutation } from '@tanstack/react-query'; |
||||||
|
import { Modal } from '../../Modal'; |
||||||
|
import { queryClient } from '../../../stores/query-client'; |
||||||
|
import { httpPost } from '../../../lib/query-http'; |
||||||
|
import { useStore } from '@nanostores/react'; |
||||||
|
import { currentRoadmap } from '../../../stores/roadmap'; |
||||||
|
import { useToast } from '../../../hooks/use-toast'; |
||||||
|
import { DateTime } from 'luxon'; |
||||||
|
|
||||||
|
type SubmitShowcaseWarningProps = { |
||||||
|
onClose: () => void; |
||||||
|
}; |
||||||
|
|
||||||
|
export function SubmitShowcaseWarning(props: SubmitShowcaseWarningProps) { |
||||||
|
const { onClose } = props; |
||||||
|
|
||||||
|
const toast = useToast(); |
||||||
|
const $currentRoadmap = useStore(currentRoadmap); |
||||||
|
|
||||||
|
const submit = useMutation( |
||||||
|
{ |
||||||
|
mutationFn: async () => { |
||||||
|
return httpPost(`/v1-submit-for-showcase/${$currentRoadmap?._id}`, {}); |
||||||
|
}, |
||||||
|
onSuccess: () => { |
||||||
|
queryClient.invalidateQueries({ |
||||||
|
queryKey: ['get-roadmap'], |
||||||
|
}); |
||||||
|
|
||||||
|
onClose(); |
||||||
|
}, |
||||||
|
onError: (error) => { |
||||||
|
toast.error(error?.message || 'Something went wrong'); |
||||||
|
}, |
||||||
|
}, |
||||||
|
queryClient, |
||||||
|
); |
||||||
|
|
||||||
|
const { |
||||||
|
showcaseStatus, |
||||||
|
showcaseRejectedReason, |
||||||
|
showcaseRejectedAt, |
||||||
|
updatedAt, |
||||||
|
} = $currentRoadmap || {}; |
||||||
|
|
||||||
|
return ( |
||||||
|
<Modal onClose={onClose}> |
||||||
|
<div className="p-4"> |
||||||
|
<h2 className="text-lg font-semibold"> |
||||||
|
{showcaseStatus === 'rejected_with_reason' |
||||||
|
? 'Rejected with Reason' |
||||||
|
: 'Feature Your Roadmap'} |
||||||
|
</h2> |
||||||
|
<p className="mt-2 text-sm"> |
||||||
|
{showcaseStatus === 'rejected_with_reason' && ( |
||||||
|
<> |
||||||
|
<span |
||||||
|
className={ |
||||||
|
'block rounded-md bg-red-100 px-2 py-1.5 text-red-700' |
||||||
|
} |
||||||
|
> |
||||||
|
{showcaseRejectedReason} |
||||||
|
</span> |
||||||
|
<span className="block mt-3"> |
||||||
|
Feel free to make changes to your roadmap and resubmit. |
||||||
|
</span> |
||||||
|
</> |
||||||
|
)} |
||||||
|
{!showcaseStatus && ( |
||||||
|
<> |
||||||
|
We will review your roadmap and if accepted, we will make it |
||||||
|
public and show it on the community roadmap listing.{' '} |
||||||
|
<span className="mt-4 block font-medium"> |
||||||
|
Are you sure to submit? |
||||||
|
</span> |
||||||
|
</> |
||||||
|
)} |
||||||
|
</p> |
||||||
|
|
||||||
|
<div className="mt-4 grid grid-cols-2 gap-2"> |
||||||
|
<button |
||||||
|
className="flex-grow cursor-pointer rounded-lg bg-gray-200 py-2 text-center text-sm hover:bg-gray-300" |
||||||
|
onClick={onClose} |
||||||
|
disabled={submit.isPending} |
||||||
|
> |
||||||
|
Cancel |
||||||
|
</button> |
||||||
|
<button |
||||||
|
className="w-full rounded-lg bg-gray-900 py-2 text-sm text-white hover:bg-gray-800 disabled:cursor-not-allowed disabled:opacity-60" |
||||||
|
disabled={submit.isPending} |
||||||
|
onClick={() => { |
||||||
|
const updatedAtDate = |
||||||
|
updatedAt && DateTime.fromJSDate(new Date(updatedAt)); |
||||||
|
const showcaseRejectedAtDate = |
||||||
|
showcaseRejectedAt && |
||||||
|
DateTime.fromJSDate(new Date(showcaseRejectedAt)); |
||||||
|
|
||||||
|
if ( |
||||||
|
showcaseRejectedAtDate && |
||||||
|
updatedAtDate && |
||||||
|
updatedAtDate < showcaseRejectedAtDate |
||||||
|
) { |
||||||
|
toast.error( |
||||||
|
'You need to make changes to your roadmap before resubmitting.', |
||||||
|
); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
submit.mutate(); |
||||||
|
}} |
||||||
|
> |
||||||
|
{submit.isPending |
||||||
|
? 'Submitting...' |
||||||
|
: showcaseStatus === 'rejected_with_reason' |
||||||
|
? 'Resubmit' |
||||||
|
: 'Submit'} |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</Modal> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
import { useQuery } from '@tanstack/react-query'; |
||||||
|
import type { GetRoadmapResponse } from '../components/CustomRoadmap/CustomRoadmap'; |
||||||
|
import { httpGet, type FetchError } from '../lib/query-http'; |
||||||
|
import { queryClient } from '../stores/query-client'; |
||||||
|
|
||||||
|
type UseCustomRoadmapOptions = { |
||||||
|
slug?: string; |
||||||
|
id?: string; |
||||||
|
secret?: string; |
||||||
|
}; |
||||||
|
|
||||||
|
export function useCustomRoadmap(options: UseCustomRoadmapOptions) { |
||||||
|
const { slug, id, secret } = options; |
||||||
|
|
||||||
|
return useQuery<GetRoadmapResponse, FetchError>( |
||||||
|
{ |
||||||
|
queryKey: [ |
||||||
|
'get-roadmap', |
||||||
|
{ |
||||||
|
slug, |
||||||
|
id, |
||||||
|
}, |
||||||
|
], |
||||||
|
queryFn: async () => { |
||||||
|
const roadmapUrl = slug |
||||||
|
? new URL( |
||||||
|
`${import.meta.env.PUBLIC_API_URL}/v1-get-roadmap-by-slug/${slug}`, |
||||||
|
) |
||||||
|
: new URL(`${import.meta.env.PUBLIC_API_URL}/v1-get-roadmap/${id}`); |
||||||
|
|
||||||
|
if (secret) { |
||||||
|
roadmapUrl.searchParams.set('secret', secret); |
||||||
|
} |
||||||
|
|
||||||
|
return httpGet(roadmapUrl.toString()); |
||||||
|
}, |
||||||
|
enabled: !!(slug || id), |
||||||
|
}, |
||||||
|
queryClient, |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,146 @@ |
|||||||
|
import Cookies from 'js-cookie'; |
||||||
|
import fp from '@fingerprintjs/fingerprintjs'; |
||||||
|
import { TOKEN_COOKIE_NAME, removeAuthToken } from './jwt.ts'; |
||||||
|
|
||||||
|
type HttpOptionsType = RequestInit; |
||||||
|
|
||||||
|
type AppResponse = Record<string, any>; |
||||||
|
|
||||||
|
export interface FetchError extends Error { |
||||||
|
status: number; |
||||||
|
message: string; |
||||||
|
} |
||||||
|
|
||||||
|
type AppError = { |
||||||
|
status: number; |
||||||
|
message: string; |
||||||
|
errors?: { message: string; location: string }[]; |
||||||
|
}; |
||||||
|
|
||||||
|
type ApiReturn<ResponseType> = ResponseType; |
||||||
|
|
||||||
|
/** |
||||||
|
* Wrapper around fetch to make it easy to handle errors |
||||||
|
* |
||||||
|
* @param url |
||||||
|
* @param options |
||||||
|
*/ |
||||||
|
export async function httpCall<ResponseType = AppResponse>( |
||||||
|
url: string, |
||||||
|
options?: HttpOptionsType, |
||||||
|
): Promise<ApiReturn<ResponseType>> { |
||||||
|
const fullUrl = url.startsWith('http') |
||||||
|
? url |
||||||
|
: `${import.meta.env.PUBLIC_API_URL}${url}`; |
||||||
|
try { |
||||||
|
const fingerprintPromise = await fp.load(); |
||||||
|
const fingerprint = await fingerprintPromise.get(); |
||||||
|
|
||||||
|
const isMultiPartFormData = options?.body instanceof FormData; |
||||||
|
|
||||||
|
const headers = new Headers({ |
||||||
|
Accept: 'application/json', |
||||||
|
Authorization: `Bearer ${Cookies.get(TOKEN_COOKIE_NAME)}`, |
||||||
|
fp: fingerprint.visitorId, |
||||||
|
...(options?.headers ?? {}), |
||||||
|
}); |
||||||
|
|
||||||
|
if (!isMultiPartFormData) { |
||||||
|
headers.set('Content-Type', 'application/json'); |
||||||
|
} |
||||||
|
|
||||||
|
const response = await fetch(fullUrl, { |
||||||
|
credentials: 'include', |
||||||
|
...options, |
||||||
|
headers, |
||||||
|
}); |
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const doesAcceptHtml = options?.headers?.['Accept'] === 'text/html'; |
||||||
|
|
||||||
|
const data = doesAcceptHtml ? await response.text() : await response.json(); |
||||||
|
|
||||||
|
// Logout user if token is invalid
|
||||||
|
if (data?.status === 401) { |
||||||
|
removeAuthToken(); |
||||||
|
window.location.href = '/login'; |
||||||
|
return null as unknown as ApiReturn<ResponseType>; |
||||||
|
} |
||||||
|
|
||||||
|
if (!response.ok) { |
||||||
|
if (data.errors) { |
||||||
|
const error = new Error() as FetchError; |
||||||
|
error.message = data.message; |
||||||
|
error.status = response?.status; |
||||||
|
throw error; |
||||||
|
} else { |
||||||
|
throw new Error('An unexpected error occurred'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return data as ResponseType; |
||||||
|
} catch (error: any) { |
||||||
|
throw error; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export async function httpPost<ResponseType = AppResponse>( |
||||||
|
url: string, |
||||||
|
body: Record<string, any>, |
||||||
|
options?: HttpOptionsType, |
||||||
|
): Promise<ApiReturn<ResponseType>> { |
||||||
|
return httpCall<ResponseType>(url, { |
||||||
|
...options, |
||||||
|
method: 'POST', |
||||||
|
body: body instanceof FormData ? body : JSON.stringify(body), |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
export async function httpGet<ResponseType = AppResponse>( |
||||||
|
url: string, |
||||||
|
queryParams?: Record<string, any>, |
||||||
|
options?: HttpOptionsType, |
||||||
|
): Promise<ApiReturn<ResponseType>> { |
||||||
|
const searchParams = new URLSearchParams(queryParams).toString(); |
||||||
|
const queryUrl = searchParams ? `${url}?${searchParams}` : url; |
||||||
|
|
||||||
|
return httpCall<ResponseType>(queryUrl, { |
||||||
|
credentials: 'include', |
||||||
|
method: 'GET', |
||||||
|
...options, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
export async function httpPatch<ResponseType = AppResponse>( |
||||||
|
url: string, |
||||||
|
body: Record<string, any>, |
||||||
|
options?: HttpOptionsType, |
||||||
|
): Promise<ApiReturn<ResponseType>> { |
||||||
|
return httpCall<ResponseType>(url, { |
||||||
|
...options, |
||||||
|
method: 'PATCH', |
||||||
|
body: JSON.stringify(body), |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
export async function httpPut<ResponseType = AppResponse>( |
||||||
|
url: string, |
||||||
|
body: Record<string, any>, |
||||||
|
options?: HttpOptionsType, |
||||||
|
): Promise<ApiReturn<ResponseType>> { |
||||||
|
return httpCall<ResponseType>(url, { |
||||||
|
...options, |
||||||
|
method: 'PUT', |
||||||
|
body: JSON.stringify(body), |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
export async function httpDelete<ResponseType = AppResponse>( |
||||||
|
url: string, |
||||||
|
options?: HttpOptionsType, |
||||||
|
): Promise<ApiReturn<ResponseType>> { |
||||||
|
return httpCall<ResponseType>(url, { |
||||||
|
...options, |
||||||
|
method: 'DELETE', |
||||||
|
}); |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
import { QueryCache, QueryClient } from '@tanstack/react-query'; |
||||||
|
|
||||||
|
export const queryClient = new QueryClient({ |
||||||
|
queryCache: new QueryCache({}), |
||||||
|
defaultOptions: { |
||||||
|
queries: { |
||||||
|
retry: false, |
||||||
|
enabled: !import.meta.env.SSR, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
Loading…
Reference in new issue