Skip to content

Commit

Permalink
Merge pull request #11 from johanesPao/dev
Browse files Browse the repository at this point in the history
Refaktor kueri rute /tulisan/id dan UI TulisanCard, Tulisan
  • Loading branch information
johanesPao authored May 22, 2024
2 parents 5d5ef1a + e795942 commit c800d55
Show file tree
Hide file tree
Showing 14 changed files with 220 additions and 133 deletions.
5 changes: 5 additions & 0 deletions api/src/kesalahan_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ pub enum KesalahanApi {
#[error("DatabaseError: {0}")]
KesalahanDatabase(surrealdb::Error),

#[allow(dead_code)]
#[error("Not Found")]
TidakDitemukan,

#[allow(dead_code)]
#[error("{0}")]
BadRequest(String),
Expand Down Expand Up @@ -69,6 +73,7 @@ impl ResponseError for KesalahanApi {
fn status_code(&self) -> StatusCode {
match self {
KesalahanApi::KesalahanDatabase(_) => StatusCode::INTERNAL_SERVER_ERROR,
KesalahanApi::TidakDitemukan => StatusCode::NOT_FOUND,
KesalahanApi::KesalahanIO(_) => StatusCode::INTERNAL_SERVER_ERROR,
KesalahanApi::KesalahanInternal(_) => StatusCode::INTERNAL_SERVER_ERROR,
KesalahanApi::BadRequest(_) => StatusCode::BAD_REQUEST,
Expand Down
23 changes: 19 additions & 4 deletions api/src/rute/tulisan.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
kesalahan_api::KesalahanApi,
surrealdb::model::tulisan::TulisanDiterbitkan,
surrealdb::model::tulisan::Tulisan,
};
use actix_web::{
get,
Expand All @@ -10,20 +10,35 @@ use actix_web::{
use serde_json::json;
use surrealdb::{engine::remote::ws::Client, Surreal};

#[get("/")]
#[get("/diterbitkan")]
pub async fn get_semua_diterbitkan(
db: Data<Surreal<Client>>
) -> Result<HttpResponse, KesalahanApi> {
let tulisan = TulisanDiterbitkan::get_semua_diterbitkan(&db).await?;
let tulisan = Tulisan::get_semua_diterbitkan(&db).await?;

Ok(HttpResponse::Ok().json(json!(tulisan)))
}

#[get("/{id}")]
pub async fn get_tulisan(
path: web::Path<String>,
db: Data<Surreal<Client>>
) -> Result<HttpResponse, KesalahanApi> {
let id_tulisan = path.into_inner();
let respon_tulisan = Tulisan::get_tulisan_id(id_tulisan.as_str(), &db).await?;

match respon_tulisan {
Some(tulisan) => Ok(HttpResponse::Ok().json(json!(tulisan))),
None => Err(KesalahanApi::TidakDitemukan)
}
}

pub fn konfigurasi(konfig: &mut web::ServiceConfig) {
konfig
.service(
web::scope("/tulisan-diterbitkan")
web::scope("/tulisan")
// .service(cek_koneksi_db)
.service(get_semua_diterbitkan)
.service(get_tulisan)
);
}
33 changes: 28 additions & 5 deletions api/src/surrealdb/model/tulisan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::kesalahan_api::KesalahanApi;
// const NAMA_TABEL : &str= "tulisan";

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TulisanDiterbitkan {
pub struct Tulisan {
pub id: Thing,
pub judul: String,
pub penulis: String,
Expand Down Expand Up @@ -57,7 +57,7 @@ impl Into<Value> for Kategori {
}
}

impl Into<Value> for TulisanDiterbitkan {
impl Into<Value> for Tulisan {
fn into(self) -> Value {
let mut map = Object::default();
map.insert("id".to_string(), Value::Thing(self.id));
Expand All @@ -71,9 +71,9 @@ impl Into<Value> for TulisanDiterbitkan {
}
}

impl TulisanDiterbitkan {
impl Tulisan {
/// Kueri dan kembalikan semua tulisan yang sudah diterbitkan dalam database
pub async fn get_semua_diterbitkan(db: &Data<Surreal<Client>>) -> Result<Vec<TulisanDiterbitkan>, KesalahanApi> {
pub async fn get_semua_diterbitkan(db: &Data<Surreal<Client>>) -> Result<Vec<Tulisan>, KesalahanApi> {
let kueri =
"SELECT
id,
Expand All @@ -91,8 +91,31 @@ impl TulisanDiterbitkan {

let mut respon = db.query(kueri).await?;

let tulisan = respon.take::<Vec<TulisanDiterbitkan>>(0)?;
let tulisan = respon.take::<Vec<Tulisan>>(0)?;

Ok(tulisan)
}
/// Kueri dan kembalikan tulisan dengan spesifik id
pub async fn get_tulisan_id(id_tulisan: &str, db: &Data<Surreal<Client>>) -> Result<Option<Tulisan>, KesalahanApi> {
let kueri = format!(
"SELECT
id,
judul,
penulis.nama_tampilan as penulis,
kategori,
konten,
dibuat,
dimodifikasi
FROM tulisan
WHERE id = tulisan:{0}
FETCH akun, kategori",
id_tulisan
);

let mut respon = db.query(kueri).await?;

let tulisan = respon.take::<Option<Tulisan>>(0)?;

Ok(tulisan)
}
}
75 changes: 75 additions & 0 deletions ui/src/fungsi/tulisan.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { ResponTulisanProps, TulisanProps } from "../props/Tulisan.props";

export const fetchTulisanDiterbitkan = async (
setDaftarTulisan: React.Dispatch<React.SetStateAction<TulisanProps[] | null>>
) => {
try {
const APIEndpoint = import.meta.env.DEV ? import.meta.env.VITE_DEV_API : import.meta.env.VITE_PROD_API;
const APIUrl = `${APIEndpoint}/tulisan/diterbitkan`
const respon = await fetch(`${APIUrl}`, {
method: 'GET'
})

// return eraly jika gagal detch
if (!respon.ok) {
throw new Error(`Gagal fetch data dari ${APIUrl}`)
}

// ubah respon menjadi json dan iterasi ektraksi tulisan
const data: [] = await respon.json();
let arrayTulisan: TulisanProps[] = [];
if (data.length > 0) {
data.map((tulisan: ResponTulisanProps) => {
const dataTulisan: TulisanProps = {
id: tulisan.id.id.String,
judul: tulisan.judul,
penulis: tulisan.penulis,
kategori: tulisan.kategori,
konten: tulisan.konten,
dibuat: new Date(tulisan.dibuat),
dimodifikasi: new Date(tulisan.dimodifikasi)
};
arrayTulisan.push(dataTulisan)
});
};
setDaftarTulisan(arrayTulisan);
} catch (kesalahan) {
console.error(kesalahan);
}
};

export const fetchTulisan = async (
id: string,
setTulisan: React.Dispatch<React.SetStateAction<TulisanProps | null>>
) => {
try {
const APIEndpoint = import.meta.env.DEV ? import.meta.env.VITE_DEV_API : import.meta.env.VITE_PROD_API;
const APIUrl = `${APIEndpoint}/tulisan/${id}`
const respon = await fetch(`${APIUrl}`, {
method: 'GET'
})

// return eraly jika gagal detch
if (!respon.ok) {
console.log(respon);
throw new Error(`Gagal fetch data dari ${APIUrl}: ${respon}`)
}

// ubah respon menjadi json dan set tulisan
const data = await respon.json();
let tulisan: TulisanProps = {
id: data.id.id.String,
judul: data.judul,
penulis: data.penulis,
kategori: data.kategori,
konten: data.konten,
dibuat: new Date(data.dibuat),
dimodifikasi: new Date(data.dimodifikasi)
};

// set stat tulisan
setTulisan(tulisan);
} catch (kesalahan) {
console.log(kesalahan);
}
};
19 changes: 4 additions & 15 deletions ui/src/halaman/Landing.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,15 @@
import { Suspense, lazy } from 'react';

import BodyLanding from "../komponen/BodyLanding";
import HeaderLanding from "../komponen/HeaderLanding";
import LayarMemuat from '../komponen/LayarMemuat';

const Loadable = (Component: any) => (props: JSX.IntrinsicAttributes) =>
(
<Suspense fallback={<LayarMemuat />}>
<Component {...props} />
</Suspense>
)

const Body = Loadable(lazy(() => import("../komponen/BodyLanding")))

const Landing = () => {
return (
<>
<div className="flex flex-col xl:flex-row bg-stone-900 min-h-screen py-0 px-0">
<div className="xl:w-96">
<div className="fixed xl:w-96">
<HeaderLanding/>
</div>
<div className="xl:grow">
<Body/>
<div className="xl:pl-96 pt-44 xl:pt-0 xl:grow">
<BodyLanding/>
</div>
</div>
</>
Expand Down
11 changes: 11 additions & 0 deletions ui/src/interface.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
export interface IRepoData {
namaRepo: string,
dataRepo: []
}

// const interface
export const opsiStringDate: Intl.DateTimeFormatOptions = {
year: "numeric",
month: "short",
day: "numeric",
hour: "numeric",
hourCycle: "h12",
dayPeriod: "short",
timeZone: "Asia/Jakarta"
}
43 changes: 4 additions & 39 deletions ui/src/komponen/DaftarTulisan.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,15 @@
import { useEffect, useState } from "react";
import { ResponTulisanProps, TulisanProps } from "../props/Tulisan.props";
import { TulisanProps } from "../props/Tulisan.props";
import TulisanCard from "./TulisanCard";
import { fetchTulisanDiterbitkan } from "../fungsi/tulisan";

const DaftarTulisan = () => {
const [daftarTulisan, setDaftarTulisan] = useState<TulisanProps[] | null>(null)

useEffect(() => {
const fetchTulisan = async () => {
try {
const APIEndpoint = import.meta.env.DEV ? import.meta.env.VITE_DEV_API : import.meta.env.VITE_PROD_API;
const APIUrl = `${APIEndpoint}/tulisan-diterbitkan/`
const respon = await fetch(`${APIUrl}`, {
method: 'GET'
})

// return eraly jika gagal detch
if (!respon.ok) {
throw new Error(`Gagal fetch data dari ${APIUrl}`)
}

// ubah respon menjadi json dan iterasi ektraksi tulisan
const data: [] = await respon.json();
let arrayTulisan: TulisanProps[] = [];
if (data.length > 0) {
data.map((tulisan: ResponTulisanProps) => {
const dataTulisan: TulisanProps = {
id: tulisan.id.id.String,
judul: tulisan.judul,
penulis: tulisan.penulis,
kategori: tulisan.kategori,
konten: tulisan.konten,
dibuat: new Date(tulisan.dibuat),
dimodifikasi: new Date(tulisan.dimodifikasi)
};
arrayTulisan.push(dataTulisan)
});
};
setDaftarTulisan(arrayTulisan);
} catch (kesalahan) {
console.error(kesalahan);
}
};

fetchTulisan();

fetchTulisanDiterbitkan(setDaftarTulisan);
}, [])

return (
<div>
{daftarTulisan !== null && daftarTulisan.map(tulisan => {
Expand Down
2 changes: 1 addition & 1 deletion ui/src/komponen/HeaderLanding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import HeaderLandingContent from "./HeaderLandingContent";
const HeaderLanding = () => {
return (
<>
<div className="flex flex-col bg-primer text-teks h-42 w-screen px-10 py-10 xl:w-96 xl:h-screen">
<div className="flex flex-col bg-primer text-teks h-44 w-screen px-10 py-10 xl:w-96 xl:h-screen">
<div className="h-full flex justify-center items-center xl:justify-end">
<HeaderLandingContent/>
</div>
Expand Down
8 changes: 4 additions & 4 deletions ui/src/komponen/HeaderLandingContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ const HeaderLandingContent = () => {
url: "/"
},
{
teks: "archive",
url: "/archive"
teks: "archives",
url: "/archives"
},
{
teks: "tags",
url: "/tags"
teks: "categories",
url: "/categories"
},
{
teks: "cv",
Expand Down
24 changes: 24 additions & 0 deletions ui/src/komponen/KategoriCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { IconStack2 } from "@tabler/icons-react";
import { ResponKategoriProps } from "../props/Tulisan.props";

const KategoriCard = ({props}: {props:ResponKategoriProps}) => {
const lihatKategori = (id: string) => {
console.log(`Lihat tulisan dengan kategori id kategori:${id}`)
}

return (
<div
className="flex flex-row gap-1 items-center italic text-sm group cursor-pointer"
onClick={() => lihatKategori(props.id.id.String)}
>
<div>
<IconStack2 color={`${props.warna}`}/>
</div>
<div className="text-zinc-300">
{props.deskripsi}
</div>
</div>
)
}

export default KategoriCard;
Loading

0 comments on commit c800d55

Please sign in to comment.