Skip to content

Commit b2375d5

Browse files
committedMar 9, 2025··
Lagt til støtte for å slette innslag
1 parent 2267caa commit b2375d5

File tree

13 files changed

+60
-26
lines changed

13 files changed

+60
-26
lines changed
 

‎backend/src/main/kotlin/no/nais/cloud/testnais/sandbox/bachelorurlforkorter/UrlForkorterApi.kt

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ fun startAppServer(config: Config) {
3030
path("api") {
3131
post("sjekk", UrlForkorterController::sjekk, Rolle.Alle)
3232
post("forkort", UrlForkorterController::forkort, Rolle.Alle)
33+
post("slett", UrlForkorterController::slett, Rolle.Alle)
3334
get("hentalle", UrlForkorterController::hentAlleMedMetadata, Rolle.Alle)
3435
}
3536
get("{korturl}") { ctx ->

‎backend/src/main/kotlin/no/nais/cloud/testnais/sandbox/bachelorurlforkorter/UrlForkorterController.kt

+11-4
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@ private val logger = KotlinLogging.logger {}
99

1010
object UrlForkorterController {
1111

12-
fun test(ctx: Context) {
13-
ctx.result("Hello world!")
14-
}
15-
1612
fun redirect(ctx: Context) {
1713
val korturl = ctx.pathParam("korturl")
1814
if (!korturl.matches(Regex("^[a-z0-9]{6}$"))) {
@@ -78,4 +74,15 @@ object UrlForkorterController {
7874
ctx.status(500)
7975
}
8076
}
77+
78+
fun slett(ctx: Context) {
79+
val id = ctx.queryParam("id")
80+
try {
81+
ShortUrlDataAccessObject.deleteShortUrlById(Integer.parseInt(id))
82+
ctx.status(204)
83+
} catch (e: Exception) {
84+
logger.error("Feil ved sletting av URL", e)
85+
ctx.status(500)
86+
}
87+
}
8188
}

‎backend/src/main/kotlin/no/nais/cloud/testnais/sandbox/bachelorurlforkorter/common/db/ShortUrlDataAccessObject.kt

+8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package no.nais.cloud.testnais.sandbox.bachelorurlforkorter.common.db
22

33
import org.jetbrains.exposed.sql.*
44
import org.jetbrains.exposed.sql.transactions.transaction
5+
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
56

67
object ShortUrlDataAccessObject {
78
fun storeShortUrl(shortUrl: String, longUrl: String, createdBy: String?) {
@@ -52,4 +53,11 @@ object ShortUrlDataAccessObject {
5253
}
5354
}
5455
}
56+
57+
fun deleteShortUrlById(id: Int): Boolean {
58+
return transaction {
59+
ShortUrls.deleteWhere { ShortUrls.id eq id } > 0
60+
}
61+
}
62+
5563
}

‎frontend/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<html lang="en">
33
<head>
44
<meta charset="UTF-8" />
5-
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
5+
<link rel="icon" type="image/svg+xml" href="" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
77
<title>Url-forkorter</title>
88
</head>

‎frontend/public/icons/close.svg

+3
Loading

‎frontend/public/vite.svg

-1
This file was deleted.

‎frontend/src/assets/react.svg

-1
This file was deleted.

‎frontend/src/components/CreateShortUrl.tsx

+2-10
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,9 @@ export default function CreateShortUrl() {
99
const [inputValue, setInputValue] = useState("");
1010
const [result, setResult] = useState<string | null>(null);
1111

12-
async function postShortUrlSearch(longUrl: string) {
13-
try {
14-
return await apiRequest<{ forkortetUrl: string }>(`forkort?langurl=${longUrl}`, "POST");
15-
} catch (error) {
16-
console.error("API error:", error);
17-
}
18-
}
19-
2012
function handleCreateClick() {
2113
if (!isValidUrl(inputValue)) return;
22-
postShortUrlSearch(inputValue).then((res) => {
14+
apiRequest<{ forkortetUrl: string }>(`forkort?langurl=${inputValue}`, "POST").then((res) => {
2315
if (res) setResult(res.forkortetUrl);
2416
}).catch(error => {
2517
setResult(null);
@@ -36,7 +28,7 @@ export default function CreateShortUrl() {
3628
onChange={setInputValue}>
3729
</Input>
3830
{result &&
39-
<a href={url} target="_blank">{url}</a>
31+
<a href={url} target="_blank">{url}</a>
4032
}
4133
</>
4234
)

‎frontend/src/components/SearchShortUrl.tsx

+1-9
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,13 @@ export default function SearchShortUrl() {
77
const [inputValue, setInputValue] = useState("");
88
const [searchResult, setSearchResult] = useState<string | null>(null);
99

10-
async function postShortUrlSearch(shortUrl: string) {
11-
try {
12-
return await apiRequest<{langurl: string }>(`sjekk?korturl=${shortUrl}`, "POST");
13-
} catch (error) {
14-
console.error("API error:", error);
15-
}
16-
}
17-
1810
function handleSearchClick() {
1911
const shortUrl = extractShortUrl(inputValue)
2012
if (shortUrl === null || shortUrl.length !== 6) {
2113
console.log("test")
2214
return;
2315
}
24-
postShortUrlSearch(shortUrl).then((res) => {
16+
apiRequest<{langurl: string }>(`sjekk?korturl=${shortUrl}`, "POST").then((res) => {
2517
if (res) setSearchResult(res.langurl);
2618
else setSearchResult(null);
2719
}).catch(error => {

‎frontend/src/components/ShowAll/ShowAll.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
TableHeaderCell,
1010
TableRow
1111
} from "./showall.style.ts";
12+
import Icon from "../shared/Icon/Icon.tsx";
1213

1314
interface UrlData {
1415
id: number;
@@ -38,6 +39,14 @@ export default function ShowAllUrls() {
3839
fetchUrls().then(() => setLoading(false));
3940
}, []);
4041

42+
function handleDeleteClick(id: number) {
43+
apiRequest<{ forkortetUrl: string }>(`slett?id=${id}`, "POST")
44+
.then(() => setUrls((prevUrls) => prevUrls.filter((url) => url.id !== id)))
45+
.catch((error: Error) => {
46+
console.error("API error:", error);
47+
});
48+
}
49+
4150
return (
4251
<TableContainer>
4352
{loading && <p>Loading...</p>}
@@ -53,6 +62,7 @@ export default function ShowAllUrls() {
5362
<TableHeaderCell>Opprettet</TableHeaderCell>
5463
<TableHeaderCell>Av bruker</TableHeaderCell>
5564
<TableHeaderCell>Antall besøk</TableHeaderCell>
65+
<TableHeaderCell>Slett</TableHeaderCell>
5666
</TableRow>
5767
</TableHeader>
5868
<tbody>
@@ -72,6 +82,7 @@ export default function ShowAllUrls() {
7282
<TableCell>{new Date(url.createdAt).toLocaleString()}</TableCell>
7383
<TableCell>{url.createdBy || "Unknown"}</TableCell>
7484
<TableCell>{url.clicks}</TableCell>
85+
<TableCell><Icon icon="close" onClick={() => {handleDeleteClick(url.id)}}></Icon></TableCell>
7586
</TableRow>
7687
))}
7788
</tbody>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import {StyledIcon} from "./icon.style.ts";
2+
3+
interface Props {
4+
icon: string;
5+
onClick?: () => void;
6+
color?: string;
7+
}
8+
9+
export default function Icon({icon, onClick}: Props) {
10+
return (
11+
<StyledIcon src={`icons/${icon}.svg`} onClick={onClick}/>
12+
)
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import styled from "styled-components";
2+
3+
export const StyledIcon = styled.img`
4+
height: 1.1rem;
5+
&:hover {
6+
cursor: pointer;
7+
}
8+
`

‎frontend/src/util/api/apiRequest.ts

+1
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ export async function apiRequest<T>(
1919
throw new Error(`Error ${response.status}: ${response.statusText}`);
2020
}
2121

22+
if (response.status === 204) return null as T;
2223
return response.json();
2324
}

0 commit comments

Comments
 (0)
Please sign in to comment.