Skip to content

Commit e5e8f05

Browse files
committed
Added frontend logic to integrate with backend
1 parent 32a8865 commit e5e8f05

File tree

8 files changed

+206
-4
lines changed

8 files changed

+206
-4
lines changed

Backend/src/main/java/com/jashgopani/urlshortner/config/WebConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public WebMvcConfigurer corsConfigurer() {
1313
return new WebMvcConfigurer() {
1414
@Override
1515
public void addCorsMappings(CorsRegistry registry) {
16-
registry.addMapping("**")
16+
registry.addMapping("/**")
1717
.allowedOriginPatterns("*")
1818
.allowedMethods("*")
1919
.allowedHeaders("*")

Backend/src/main/java/com/jashgopani/urlshortner/slug/controller/SlugController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public String index() {
3737
}
3838

3939
@PostMapping("/")
40-
public ResponseEntity<?> shortenURL(@RequestParam String url) {
40+
public ResponseEntity<?> shortenURL(@RequestParam("url") String url) {
4141
try {
4242
Slug slug = slugService.generateSlug(url);
4343
return ResponseEntity.status(HttpStatus.CREATED).body(slug);

Frontend/index.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
</head>
1414
<body>
1515
<div id="root"></div>
16+
<script
17+
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
18+
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
19+
crossorigin="anonymous"></script>
1620
<script type="module" src="/src/main.jsx"></script>
1721
</body>
1822
</html>

Frontend/src/App.jsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,30 @@
1-
import viteLogo from "/vite.svg";
21
import "./App.css";
2+
import URLShortnerForm from "./slug/components/URLShortnerForm";
3+
import { useState, useEffect } from "react";
4+
import { fetchSlugs, initializeStorage } from "./slug/utils/service";
5+
import SlugList from "./slug/components/SlugList";
36

47
function App() {
8+
const [shortURLList, setShortURLList] = useState([]);
9+
useEffect(() => {
10+
initializeStorage();
11+
const fetchShortURLs = async () => {
12+
try {
13+
const slugs = await fetchSlugs();
14+
console.log("Fetching short urls from the backend", slugs);
15+
setShortURLList(slugs);
16+
} catch (error) {
17+
console.error("Error fetching short urls", error);
18+
}
19+
};
20+
fetchShortURLs();
21+
}, []);
22+
523
return (
624
<>
725
<div className='container'>
8-
<h1>URL Shortner</h1>
26+
<URLShortnerForm updateURLListCallback={setShortURLList} />
27+
<SlugList slugs={shortURLList} />
928
</div>
1029
</>
1130
);
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { BACKEND_URL } from "../utils/constants";
2+
3+
function SlugList({ slugs }) {
4+
return (
5+
<div className='row'>
6+
<h2>Your Shortened URL list</h2>
7+
{!slugs && <div className='alert alert-info'>No URLs shortened yet</div>}
8+
9+
{slugs &&
10+
slugs.map((slug) => (
11+
<div key={slug.id} className='card my-2 p-0'>
12+
<div className='card-header d-flex flex-row justify-content-between'>
13+
<a
14+
href={`${BACKEND_URL}/${slug.id}`}
15+
target='_blank'
16+
rel='noopener noreferrer'>{`${BACKEND_URL}/${slug.id}`}</a>
17+
<a
18+
className='btn btn-success btn-sm'
19+
href={`${BACKEND_URL}/${slug.id}`}
20+
target='_blank'
21+
rel='noopener noreferrer'>
22+
Open URL <i className='fas fa-external-link-alt'></i>
23+
</a>
24+
</div>
25+
<div className='card-body '>
26+
<a href={`${slug.url}`} target='_blank' rel='noopener noreferrer'>{`${slug.url}`}</a>
27+
</div>
28+
</div>
29+
))}
30+
</div>
31+
);
32+
}
33+
34+
export default SlugList;
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { useState, useEffect } from "react";
2+
import { shortenURL } from "../utils/service";
3+
4+
function URLShortnerForm({ updateURLListCallback }) {
5+
const [inputURL, setInputURL] = useState("");
6+
const [errors, setErrors] = useState([]);
7+
8+
useEffect(() => {
9+
if (errors.length > 0) {
10+
const timer = setTimeout(() => {
11+
setErrors([]);
12+
}, 5000);
13+
return () => clearTimeout(timer);
14+
}
15+
}, [errors]);
16+
17+
const handleSubmit = async (e) => {
18+
e.preventDefault();
19+
console.log("Form submitted", inputURL);
20+
const response = await shortenURL(inputURL);
21+
if (response.error) {
22+
setErrors([...errors, response.error]);
23+
} else {
24+
setInputURL("");
25+
updateURLListCallback((prevURLList) => [...prevURLList, response]);
26+
}
27+
};
28+
return (
29+
<>
30+
{errors.length > 0 && (
31+
<div className='row my-2'>
32+
<div className='alert alert-danger alert-dismissible fade show' role='alert'>
33+
{errors.map((error, index) => (
34+
<div key={index}>{error}</div>
35+
))}
36+
</div>
37+
</div>
38+
)}
39+
<div className='row my-2'>
40+
<h1>URL Shortner</h1>
41+
<form onSubmit={handleSubmit}>
42+
<div className='mb-3'>
43+
<label htmlFor='url' className='form-label'>
44+
Orignal URL
45+
</label>
46+
<input
47+
type='url'
48+
className='form-control'
49+
id='url'
50+
name='url'
51+
aria-describedby='originalURLHelp'
52+
onChange={(e) => setInputURL(e.target.value)}
53+
/>
54+
<div id='originalURLHelp' className='form-text'>
55+
The URL you want to shorten
56+
</div>
57+
</div>
58+
<div className='d-grid'>
59+
<button type='submit' className='btn btn-primary btn-large'>
60+
Shorten
61+
</button>
62+
</div>
63+
</form>
64+
</div>
65+
</>
66+
);
67+
}
68+
export default URLShortnerForm;

Frontend/src/slug/utils/constants.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const BACKEND_URL = "http://localhost:8080";
2+
3+
export { BACKEND_URL };

Frontend/src/slug/utils/service.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { BACKEND_URL } from "./constants";
2+
import axios from "axios";
3+
4+
const axiosInstance = axios.create({
5+
baseURL: BACKEND_URL,
6+
});
7+
8+
/**
9+
* Initialize the session storage
10+
*/
11+
const initializeStorage = () => {
12+
sessionStorage.clear();
13+
};
14+
15+
/**
16+
* Read the slugs from the session storage
17+
* @returns {Array} slugs
18+
*/
19+
const readLocalSlugs = () => {
20+
const slugs = sessionStorage.getItem("slugs");
21+
return slugs ? JSON.parse(slugs) : [];
22+
};
23+
24+
/**
25+
* Slugs to be inserted in the session storage
26+
* @param {Array} slugs
27+
*/
28+
const writeNewSlugs = (slugs) => {
29+
const existingSlugs = readLocalSlugs();
30+
const newSlugs = [...existingSlugs, ...slugs];
31+
sessionStorage.setItem("slugs", JSON.stringify(newSlugs));
32+
};
33+
34+
/**
35+
* Fetch the slugs from the backend and cache them in the session storage
36+
* @returns {Array} slugs
37+
*/
38+
const fetchSlugs = async () => {
39+
const response = await axiosInstance.get("/slugs");
40+
if (response.status === 200) {
41+
writeNewSlugs(response.data);
42+
return response.data;
43+
} else if (response.data.error) {
44+
throw new Error(response.data.error);
45+
} else {
46+
throw new Error("Something went wrong. Please try again ");
47+
}
48+
};
49+
50+
/**
51+
* Gets a new short url for the given url and also writes it to the session storage
52+
* @param {String} url
53+
* @returns A slug object {slug:string, url:string}
54+
*/
55+
const shortenURL = async (url) => {
56+
try {
57+
const urlParams = new URLSearchParams();
58+
urlParams.append("url", url);
59+
const response = await axiosInstance.post("/", urlParams);
60+
if (response.status === 201) {
61+
const newSlug = response.data;
62+
writeNewSlugs([newSlug]);
63+
return newSlug;
64+
} else if (response.data.error) {
65+
throw new Error(response.data.error);
66+
} else {
67+
throw new Error("Something went wrong. Please try again");
68+
}
69+
} catch (error) {
70+
return { error: error.message };
71+
}
72+
};
73+
74+
export { initializeStorage, readLocalSlugs, writeNewSlugs, fetchSlugs, shortenURL };

0 commit comments

Comments
 (0)