Skip to content

Commit

Permalink
collection feature in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
indpurvesh committed Dec 28, 2024
1 parent b7ece38 commit 76008ba
Show file tree
Hide file tree
Showing 131 changed files with 2,066 additions and 1,427 deletions.
2 changes: 2 additions & 0 deletions react-admin/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { ModelTablePage } from "./pages/models/ModelTablePage";
import {ModelCreatePage} from "./pages/models/ModelCreatePage";
import { ModelEditPage } from "./pages/models/ModelEditPage";
import {ComponentTablePage} from "./pages/component/ComponentTablePage";
import {CollectionTable} from "./pages/collection/CollectionTable";

function App() {
return (
Expand All @@ -54,6 +55,7 @@ function App() {
<Route path="/admin/model" element={<ModelTablePage />} />
<Route path="/admin/model-create" element={<ModelCreatePage />} />
<Route path="/admin/model-edit/:model_id" element={<ModelEditPage />} />
<Route path="/admin/collection" element={<CollectionTable />} />
<Route path="/admin/page" element={<PageTable />} />
<Route path="/admin/page-create" element={<PageCreate />} />
<Route path="/admin/page-edit/:page_id" element={<PageEdit />} />
Expand Down
18 changes: 17 additions & 1 deletion react-admin/src/layouts/partials/AppSidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import {Link, Outlet} from "react-router-dom";
import {Menu} from "@headlessui/react";
import {useTranslation} from "react-i18next";
import {ChevronDownIcon, FilmIcon, RocketLaunchIcon, CpuChipIcon, DeviceTabletIcon} from "@heroicons/react/24/solid";
import {
ChevronDownIcon,
FilmIcon,
RocketLaunchIcon,
CpuChipIcon,
DeviceTabletIcon,
CircleStackIcon
} from "@heroicons/react/24/solid";
import {useContext} from "react";
import {ThemeContext} from "../../context/ThemeContext";

Expand Down Expand Up @@ -41,6 +48,15 @@ function AppSidebar() {
</div>
<div className="ml-2">{t("sidebar.page")}</div>
</Link>
<Link
className="flex items-center w-full py-1 px-2 mt-3 rounded relative hover:text-white hover:bg-gray-700"
to={`/admin/collection`}
>
<div className="pr-2">
<CircleStackIcon className="h-6 w-6"/>
</div>
<div className="ml-2">{t("collection")}</div>
</Link>

<Link
to={`admin/component`}
Expand Down
1 change: 1 addition & 0 deletions react-admin/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"table": "Table",
"permissions": "Permissions",
"page": "Page",
"collection": "Collection",
"role": "Role",
"general": "General",
"is_super_admin": "Is super admin",
Expand Down
131 changes: 131 additions & 0 deletions react-admin/src/pages/collection/CollectionTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import HasPermission from "../../components/HasPermission"
import {Link} from "react-router-dom"
import {useTranslation} from "react-i18next"
import {useState} from "react"
import {createColumnHelper, getCoreRowModel, SortingState, useReactTable} from "@tanstack/react-table"
import AvoRedTable from "../../components/AvoRedTable"
import {getFormattedDate} from "../../lib/common"
import _ from "lodash"
import {useCollectionTable} from "./hooks/useCollectionTable";
import {CollectionType} from "../../types/collection/CollectionType";

export const CollectionTable = (() => {
const [t] = useTranslation("global")
const [sorting, setSorting] = useState<SortingState>([]);
const [pagination, setPagination] = useState({
pageIndex: 0, //initial page index
pageSize: 10, //default page size
});

const customSorting = ((sorting: any) => {
setSorting(sorting)
})

const collection_api_table_response = useCollectionTable({
order: sorting.map((s) => `${s.id}:${s.desc ? 'DESC' : 'ASC'}`).join(','),
page: pagination.pageIndex
});
const customPagination = (async (pagination: any) => {
setPagination(pagination)
})
const collections: Array<CollectionType> = _.get(collection_api_table_response, 'data.data.data', [])

const columnHelper = createColumnHelper<CollectionType>()
const columns = [
columnHelper.accessor('id', {
cell: info => info.getValue(),
header: t("id")
}),
columnHelper.accessor('name', {
cell: info => info.getValue(),
header: t("name")
}),
columnHelper.accessor('identifier', {
cell: info => info.getValue(),
header: t("identifier")
}),
columnHelper.accessor('created_at', {
id: "created_at",
cell: info => getFormattedDate(info.getValue()),
header: t("created_at")
}),
columnHelper.accessor('created_by', {
cell: info => info.getValue(),
header: t("created_by")
}),
columnHelper.accessor('updated_at', {
cell: info => getFormattedDate(info.getValue()),
header: t("updated_at")
}),
columnHelper.accessor('updated_by', {
cell: info => info.getValue(),
header: t("updated_by")
}),
columnHelper.accessor('action', {
cell: info => {
return (
<HasPermission displayDenied={false} identifier="collection_edit">
<Link
className="font-medium text-primary-600 hover:text-primary-800"
to={`/admin/collection-edit/${info.row.original.id}`}
>
{t("edit")}
</Link>
</HasPermission>
)
},
enableSorting: false,
header: t("action"),
enableHiding: false
}),
]
const table = useReactTable({
data: collections,
columns,
getCoreRowModel: getCoreRowModel(),
rowCount: collection_api_table_response.data?.data.pagination.total,
onPaginationChange: customPagination,
manualPagination: true,
initialState: {
pagination,
columnVisibility: {
created_at: false,
created_by: false
}
},
manualSorting: true,
onSortingChange: customSorting,
state: {
sorting,
pagination
},
})

return (
<>
<div className="px-5">
<div className="flex items-center">
<div className="p-5 text-2xl font-semibold text-primary-500">
{t("collection")}
</div>
<div className="ml-auto">
<HasPermission displayDenied={false} identifier="collection_create">
<Link
className="bg-primary-600 py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"
to="/admin/collection-create"
>
{t("create")}
</Link>
</HasPermission>
</div>
</div>

<div className="overflow-x-auto">
<HasPermission identifier="collection_table">
<AvoRedTable table={table}/>
</HasPermission>
</div>
</div>
</>
)
})
35 changes: 35 additions & 0 deletions react-admin/src/pages/collection/hooks/useCollectionTable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {useQuery} from '@tanstack/react-query'
import { useAxios } from '../../../hooks/useAxios'
import _ from 'lodash'
import {useNavigate} from 'react-router-dom'
import PaginateType from "../../../types/misc/PaginateType";

export const useCollectionTable = (query: PaginateType) => {
let params: URLSearchParams = new URLSearchParams();
if (query.page && query.page > 0) {
params.append("page", query.page.toString())
}
if (query.order && query.order !== "") {
params.append("order", query.order)
}
let query_string = "";
if (params.toString() !== "") {
query_string = "?" + params.toString()
}

const client = useAxios();
const redirect = useNavigate();
return useQuery({
queryKey: ['collection-table', query],
queryFn: (async () => {
try {
return await client.get("/collection" + query_string)
} catch (error) {
if (_.get(error, 'response.status') === 401) {
localStorage.removeItem('AUTH_TOKEN')
redirect("/admin/login")
}
}
})
})
}
10 changes: 10 additions & 0 deletions react-admin/src/types/collection/CollectionType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export type CollectionType = {
id: string;
name: string;
identifier: string;
created_at: string;
updated_at: string;
created_by: string;
updated_by: string;
action?: string;
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
use std::sync::Arc;
use axum::extract::State;
use axum::Json;
use serde::Serialize;
use crate::api::handlers::admin_user::request::admin_user_forgot_password_request::AdminUserForgotPasswordRequest;
use crate::avored_state::AvoRedState;
use crate::error::{Error, Result};
use crate::models::validation_error::ErrorResponse;
use crate::responses::ApiResponse;
use axum::extract::State;
use axum::Json;
use serde::Serialize;
use std::sync::Arc;

#[derive(Serialize, Default)]
pub struct ForgotPasswordViewModel {
pub link: String
pub link: String,
}

pub async fn admin_user_forgot_password_api_handler (
pub async fn admin_user_forgot_password_api_handler(
state: State<Arc<AvoRedState>>,
Json(payload): Json<AdminUserForgotPasswordRequest>,
) -> Result<Json<ApiResponse<bool>>> {
println!("->> {:<12} - admin_user_forgot_password_api_handler", "HANDLER");
println!(
"->> {:<12} - admin_user_forgot_password_api_handler",
"HANDLER"
);

let error_messages = payload.validate()?;

if !error_messages.is_empty() {
let error_response = ErrorResponse {
status: false,
errors: error_messages
errors: error_messages,
};

return Err(Error::BadRequest(error_response));
Expand Down
44 changes: 21 additions & 23 deletions src/api/handlers/admin_user/admin_user_login_api_handler.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
use std::sync::Arc;
use crate::api::handlers::admin_user::request::authenticate_admin_user_request::AuthenticateAdminUserRequest;
use crate::avored_state::AvoRedState;
use crate::error::{Error, Result};
use crate::models::admin_user_model::AdminUserModel;
use crate::models::token_claim_model::TokenClaims;
use crate::models::validation_error::ErrorResponse;
use axum::extract::State;
use axum::http::{header, Response};
use axum::Json;
use axum_extra::extract::cookie::{Cookie, SameSite};
use jsonwebtoken::{encode, EncodingKey, Header};
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::sync::Arc;
use utoipa::ToSchema;
use crate::api::handlers::admin_user::request::authenticate_admin_user_request::AuthenticateAdminUserRequest;
use crate::avored_state::AvoRedState;
use crate::error::{Error, Result};
use crate::models::admin_user_model::AdminUserModel;
use crate::models::token_claim_model::TokenClaims;
use crate::models::validation_error::ErrorResponse;


/// Login Admin User
///
Expand All @@ -37,7 +36,7 @@ pub async fn admin_user_login_api_handler(
if !error_messages.is_empty() {
let error_response = ErrorResponse {
status: false,
errors: error_messages
errors: error_messages,
};

return Err(Error::BadRequest(error_response));
Expand All @@ -48,12 +47,11 @@ pub async fn admin_user_login_api_handler(
.find_by_email(&state.db, payload.email.to_owned())
.await?;

println!("admin user model: {:#?}", admin_user_model);

let is_password_match: bool = state
.admin_user_service
.compare_password(
payload.password.clone(),
admin_user_model.password.clone()
)?;
.compare_password(payload.password.clone(), admin_user_model.password.clone())?;

if !is_password_match {
return Err(Error::Authentication);
Expand All @@ -65,7 +63,7 @@ pub async fn admin_user_login_api_handler(
let claims: TokenClaims = TokenClaims {
sub: admin_user_model.clone().id,
name: admin_user_model.clone().full_name,
email:admin_user_model.clone().email,
email: admin_user_model.clone().email,
admin_user_model: admin_user_model.clone(),
exp,
iat,
Expand All @@ -88,7 +86,7 @@ pub async fn admin_user_login_api_handler(
let response_data = LoginResponseData {
status: true,
data: token,
admin_user: admin_user_model
admin_user: admin_user_model,
};

Ok(Json(response_data))
Expand All @@ -98,22 +96,19 @@ pub async fn admin_user_login_api_handler(
pub struct LoginResponseData {
pub status: bool,
pub data: String,
pub admin_user: AdminUserModel
pub admin_user: AdminUserModel,
}


#[cfg(test)]
mod tests {
use crate::api::rest_api_routes::tests::{get_axum_app, send_post_request};
use crate::error::Result;
use axum::body::Body;
use axum::http::StatusCode;
use tower::ServiceExt;
use crate::api::rest_api_routes::tests::{ get_axum_app, send_post_request};
use crate::error::Result;


#[tokio::test]
async fn test_admin_user_login_api_handler() -> Result<()>
{
async fn test_admin_user_login_api_handler() -> Result<()> {
let (app, _state) = get_axum_app().await.unwrap();
//@todo do a post request to a setup
// then do a post request with username and password
Expand All @@ -125,7 +120,10 @@ mod tests {
}"#,
);

let response = app.oneshot(send_post_request("/api/setup", payload)).await.unwrap();
let response = app
.oneshot(send_post_request("/api/setup", payload))
.await
.unwrap();

assert_eq!(response.status(), StatusCode::OK);
let res_b = response.into_body();
Expand Down
Loading

0 comments on commit 76008ba

Please sign in to comment.