feat: add input of long url on homepage
This commit is contained in:
8
src/api/url/types.ts
Normal file
8
src/api/url/types.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
export interface ShortenUrlRequest {
|
||||
longUrl: string;
|
||||
}
|
||||
|
||||
export interface ShortenUrlResponse {
|
||||
shortCode: string;
|
||||
}
|
||||
23
src/api/url/urlApi.ts
Normal file
23
src/api/url/urlApi.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ShortenUrlRequest, ShortenUrlResponse } from "./types";
|
||||
|
||||
export async function shortenUrlApi(
|
||||
payload: ShortenUrlRequest
|
||||
): Promise<ShortenUrlResponse> {
|
||||
/* TODO
|
||||
|
||||
const response = await fetch('https://api.minxa.lol/api/v1/url', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to shorten URL');
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
return {
|
||||
shortCode: 'Ux5vy' // Dummy value return
|
||||
}
|
||||
}
|
||||
5
src/app/hooks.ts
Normal file
5
src/app/hooks.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
|
||||
import type { RootState, AppDispatch } from './store';
|
||||
|
||||
export const useAppDispatch: () => AppDispatch = useDispatch;
|
||||
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
|
||||
@@ -1,7 +1,9 @@
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import urlReducer from '../features/url/urlSlice';
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
url: urlReducer
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
6
src/features/url/types.ts
Normal file
6
src/features/url/types.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
export interface UrlState {
|
||||
shortUrl: string;
|
||||
status: 'idle' | 'loading' | 'succeeded' | 'failed';
|
||||
error: string | null;
|
||||
}
|
||||
53
src/features/url/urlSlice.ts
Normal file
53
src/features/url/urlSlice.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit'
|
||||
import { RootState } from '../../app/store';
|
||||
import { shortenUrlApi } from '../../api/url/urlApi';
|
||||
import { UrlState } from './types';
|
||||
|
||||
export const shortenUrl = createAsyncThunk(
|
||||
'url/shortenUrl',
|
||||
async (longUrl: string) => {
|
||||
const data = await shortenUrlApi({ longUrl });
|
||||
return `https://minxa.lol/${data.shortCode}`;
|
||||
}
|
||||
);
|
||||
|
||||
const initialState: UrlState = {
|
||||
shortUrl: '',
|
||||
status: 'idle',
|
||||
error: null,
|
||||
};
|
||||
|
||||
const urlSlice = createSlice({
|
||||
name: 'url',
|
||||
initialState,
|
||||
reducers: {
|
||||
clearShortUrl(state) {
|
||||
state.shortUrl = '';
|
||||
state.status = 'idle';
|
||||
state.error = null;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder
|
||||
.addCase(shortenUrl.pending, (state) => {
|
||||
state.status = 'loading';
|
||||
state.shortUrl = '';
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(shortenUrl.fulfilled, (state, action: PayloadAction<string>) => {
|
||||
state.status = 'succeeded';
|
||||
state.shortUrl = action.payload;
|
||||
})
|
||||
.addCase(shortenUrl.rejected, (state, action) => {
|
||||
state.status = 'failed';
|
||||
state.error = action.error.message || 'Something went wrong';
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const { clearShortUrl } = urlSlice.actions;
|
||||
export const selectShortUrl = (state: RootState) => state.url.shortUrl;
|
||||
export const selectUrlStatus = (state: RootState) => state.url.status;
|
||||
export const selectUrlError = (state: RootState) => state.url.error;
|
||||
|
||||
export default urlSlice.reducer;
|
||||
@@ -1,13 +1,3 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -1,8 +1,45 @@
|
||||
import React, {useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { useAppDispatch, useAppSelector } from '../app/hooks';
|
||||
import {
|
||||
shortenUrl,
|
||||
selectShortUrl,
|
||||
selectUrlStatus,
|
||||
selectUrlError,
|
||||
clearShortUrl,
|
||||
} from '../features/url/urlSlice';
|
||||
|
||||
const Home: React.FC = () => {
|
||||
/* component level state */
|
||||
const [longUrl, setLongUrl] = useState('');
|
||||
|
||||
/* global state */
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const shortUrl = useAppSelector(selectShortUrl);
|
||||
const status = useAppSelector(selectUrlStatus);
|
||||
const error = useAppSelector(selectUrlError);
|
||||
|
||||
/* methods */
|
||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter' && longUrl.trim() !== '') {
|
||||
dispatch(shortenUrl(longUrl));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>Hello World!</div>
|
||||
<div className="min-h-screen flex items-center justify-center bg-purple-100">
|
||||
<div className="text-center space-y-6">
|
||||
<h1 className="text-5xl text-orange-500 font-pacifico">minxa.lol</h1>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Enter your long URL here"
|
||||
value={longUrl}
|
||||
onChange={(e) => setLongUrl(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
className="w-80 p-3 rounded-md text-lg border border-gray-300 shadow-sm focus:outline-none focus:ring-2 focus:ring-orange-400"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user