Tons of updates. Rags paginated tables, site tracking
This commit is contained in:
6773
llm-fe/package-lock.json
generated
6773
llm-fe/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,7 @@
|
||||
"bootstrap": "^5.3.3",
|
||||
"chroma-js": "^3.1.2",
|
||||
"formik": "^2.4.6",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"markdown-to-jsx": "^7.7.2",
|
||||
@@ -65,9 +66,11 @@
|
||||
"@types/lodash": "~4.17.13",
|
||||
"@types/react": "^18.3.16",
|
||||
"@types/react-dom": "^18.3.5",
|
||||
"@types/react-google-recaptcha": "^2.1.9",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-google-recaptcha": "^3.1.0",
|
||||
"typescript": "^4.9.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,10 @@ import FeedbackPage from './llm-fe/pages/FeedbackPage/FeedbackPage';
|
||||
import { Typography } from '@mui/material';
|
||||
import AsyncDashboard2 from './llm-fe/pages/AsyncDashboard2/AsyncDashboard2';
|
||||
import FeedbackPage2 from './llm-fe/pages/FeedbackPage2/FeedbackPage2';
|
||||
import DocumentStoragePage from './llm-fe/pages/DocumentStoragePage/DocumentStoragePage';
|
||||
import Account2 from './llm-fe/pages/Account2/Account2';
|
||||
import AnalyticsPage from './llm-fe/pages/Analytics/Analytics';
|
||||
import PasswordResetConfirmation from './llm-fe/pages/PasswordResetConfirmation/PasswordReset';
|
||||
|
||||
const ProtectedRoutes = () => {
|
||||
const { authenticated, needsNewPassword, loading } = useContext(AuthContext);
|
||||
@@ -45,6 +47,7 @@ class App extends Component {
|
||||
|
||||
<Route path={"/signin/"} Component={SignIn}/>
|
||||
<Route path={"/password_reset/"} Component={PasswordReset}/>
|
||||
<Route path={"/password_reset_confirmation/"} Component={PasswordResetConfirmation}/>
|
||||
<Route path={'/set_password/'} Component={SetPassword}/>
|
||||
|
||||
|
||||
@@ -52,6 +55,7 @@ class App extends Component {
|
||||
<Route element={<ProtectedRoutes />}>
|
||||
<Route path={"/"} index={true} Component={AsyncDashboard2}/>
|
||||
<Route path={"/account/"} Component={Account2}/>
|
||||
<Route path={"/document_storage"} Component={DocumentStoragePage}/>
|
||||
<Route path={"/terms_of_service/"} Component={TermsOfService}/>
|
||||
<Route path={"/feedback/"} Component={FeedbackPage2}/>
|
||||
<Route path={"/analytics/"} Component={AnalyticsPage} />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import axios from "axios";
|
||||
const Cookies = require('js-cookie');
|
||||
|
||||
//const baseURL = 'http://127.0.0.1:8001/api/';
|
||||
//const baseURL = 'http://127.0.0.1:8011/api/';
|
||||
const baseURL = 'https://chatbackend.aimloperations.com/api/';
|
||||
//const baseURL = process.env.REACT_APP_BACKEND_REST_API_BASE_URL;
|
||||
|
||||
@@ -14,6 +15,25 @@ export const axiosInstance = axios.create({
|
||||
}
|
||||
});
|
||||
|
||||
export const cleanAxiosInstance = axios.create({
|
||||
baseURL: baseURL,
|
||||
timeout: 5000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
});
|
||||
|
||||
export const axiosInstanceCSRF = axios.create({
|
||||
baseURL: baseURL,
|
||||
timeout: 5000,
|
||||
headers: {
|
||||
'X-CSRFToken': Cookies.get('csrftoken'), // Include CSRF token in headers
|
||||
},
|
||||
withCredentials: true,
|
||||
}
|
||||
);
|
||||
|
||||
axiosInstance.interceptors.request.use(config => {
|
||||
config.timeout = 100000;
|
||||
return config;
|
||||
|
||||
@@ -48,6 +48,10 @@ const Header2 = ({ absolute=false, light=false, isMini=false }: Header2Props): J
|
||||
navigate('/')
|
||||
}
|
||||
|
||||
const handleDocumentStorageClick = async () => {
|
||||
navigate('/document_storage/')
|
||||
}
|
||||
|
||||
const handleAccountClick = async () => {
|
||||
navigate('/account/')
|
||||
}
|
||||
@@ -77,6 +81,8 @@ const Header2 = ({ absolute=false, light=false, isMini=false }: Header2Props): J
|
||||
<MDBox sx={{marginLeft: "auto"}}>
|
||||
<Button color="inherit" onClick={handleAccountClick}>Account</Button>
|
||||
|
||||
<Button color="inherit" onClick={handleDocumentStorageClick}>Document Storage</Button>
|
||||
|
||||
<Button color="inherit" onClick={handleAnalyticsClick}>Analytics</Button>
|
||||
|
||||
<Button color="inherit" onClick={handleFeedbackClick} >Feedback</Button>
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Paper,
|
||||
TablePagination,
|
||||
} from "@mui/material";;
|
||||
|
||||
interface PaginatedTableProps<T> {
|
||||
data: T[];
|
||||
columns: {
|
||||
key: keyof T;
|
||||
label: string;
|
||||
render?: (value: any, row: T) => React.ReactNode;
|
||||
}[];
|
||||
rowsPerPageOptions?: number[];
|
||||
}
|
||||
|
||||
export function PaginatedTable<T>({
|
||||
data,
|
||||
columns,
|
||||
rowsPerPageOptions = [5, 10, 25],
|
||||
}: PaginatedTableProps<T>) {
|
||||
const [page, setPage] = useState(0);
|
||||
const [rowsPerPage, setRowsPerPage] = useState(rowsPerPageOptions[0]);
|
||||
|
||||
const handleChangePage = (event: unknown, newPage: number) => {
|
||||
setPage(newPage);
|
||||
};
|
||||
|
||||
const handleChangeRowsPerPage = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
setRowsPerPage(parseInt(event.target.value, 10));
|
||||
setPage(0);
|
||||
};
|
||||
|
||||
// Avoid a layout jump when reaching the last page with empty rows.
|
||||
const emptyRows =
|
||||
page > 0 ? Math.max(0, (1 + page) * rowsPerPage - data.length) : 0;
|
||||
|
||||
return (
|
||||
<Paper>
|
||||
<TableContainer>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
{columns.map((column) => (
|
||||
<TableCell key={column.key.toString()}>{column.label}</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{(rowsPerPage > 0
|
||||
? data.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
|
||||
: data
|
||||
).map((row, index) => (
|
||||
<TableRow key={index}>
|
||||
{columns.map((column) => (
|
||||
<TableCell key={column.key.toString()}>
|
||||
{column.render
|
||||
? column.render(row[column.key], row)
|
||||
: (row[column.key] as React.ReactNode)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
{emptyRows > 0 && (
|
||||
<TableRow style={{ height: 53 * emptyRows }}>
|
||||
<TableCell colSpan={columns.length} />
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<TablePagination
|
||||
rowsPerPageOptions={rowsPerPageOptions}
|
||||
component="div"
|
||||
count={data.length}
|
||||
rowsPerPage={rowsPerPage}
|
||||
page={page}
|
||||
onPageChange={handleChangePage}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
/>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
export default PaginatedTable;
|
||||
@@ -24,7 +24,7 @@ function WebSocketProvider({ children }) {
|
||||
delete channels.current[channel]
|
||||
}
|
||||
|
||||
const sendMessage = (message, conversation_id, file, fileType) => {
|
||||
const sendMessage = (message, conversation_id, file, fileType, modelName) => {
|
||||
if (socket && socket.readyState === WebSocket.OPEN){
|
||||
|
||||
if (file){
|
||||
@@ -40,6 +40,7 @@ function WebSocketProvider({ children }) {
|
||||
email: account?.email,
|
||||
file: base64File,
|
||||
fileType: fileType,
|
||||
modelName: modelName,
|
||||
}
|
||||
socket.send(JSON.stringify(data))
|
||||
|
||||
@@ -52,7 +53,8 @@ function WebSocketProvider({ children }) {
|
||||
conversation_id: conversation_id,
|
||||
email: account?.email,
|
||||
file: null,
|
||||
fileType: null
|
||||
fileType: null,
|
||||
modelName: modelName,
|
||||
}
|
||||
|
||||
socket.send(JSON.stringify(data))
|
||||
@@ -69,7 +71,7 @@ function WebSocketProvider({ children }) {
|
||||
if (account){
|
||||
|
||||
|
||||
//ws.current = new WebSocket(`ws://127.0.0.1:8001/ws/chat_again/`);
|
||||
//ws.current = new WebSocket(`ws://127.0.0.1:8011/ws/chat_again/`);
|
||||
ws.current = new WebSocket('wss://chatbackend.aimloperations.com/ws/chat_again/')
|
||||
//ws.current = process.env.REACT_APP_BACKEND_WS_API_BASE_URL;
|
||||
|
||||
|
||||
@@ -87,6 +87,16 @@ export class Announcement {
|
||||
}
|
||||
}
|
||||
|
||||
export interface DocumentType {
|
||||
id: number;
|
||||
name: string;
|
||||
date_uploaded: string;
|
||||
created: string;
|
||||
file: string;
|
||||
active: boolean;
|
||||
processed: boolean;
|
||||
}
|
||||
|
||||
export interface FeedbackType {
|
||||
id: number;
|
||||
title: string;
|
||||
@@ -95,7 +105,24 @@ export interface FeedbackType {
|
||||
category: string;
|
||||
}
|
||||
|
||||
export class Document {
|
||||
id: number = 0;
|
||||
name: string = '';
|
||||
file: string = '';
|
||||
date_uploaded: string = '';
|
||||
active: boolean = false;
|
||||
processed: boolean = false;
|
||||
constructor(initializer?: any){
|
||||
if(!initializer) return;
|
||||
if (initializer.id) this.id = initializer.id;
|
||||
if (initializer.name) this.name = initializer.name;
|
||||
if (initializer.file) this.file = initializer.file;
|
||||
if (initializer.active) this.active = initializer.active;
|
||||
if (initializer.date_uploaded) this.date_uploaded = initializer.date_uploaded;
|
||||
if (initializer.processed) this.processed = initializer.processed;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export class Feedback {
|
||||
id: number = 0;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Card, CardContent, Divider, InputAdornment, } from "@mui/material"
|
||||
import { Card, CardContent, Divider, InputAdornment, MenuItem, Select, } from "@mui/material"
|
||||
|
||||
import DashboardLayout from "../../ui-kit/examples/LayoutContainers/DashboardLayout"
|
||||
|
||||
@@ -29,6 +29,8 @@ import Footer from "../../components/Footer/Footer"
|
||||
import { MessageContext } from "../../contexts/MessageContext"
|
||||
import CustomSelect, { CustomSelectItem } from "../../components/CustomSelect/CustomSelect"
|
||||
|
||||
const MODELS = ["Turbo","RAG"]
|
||||
|
||||
type RenderMessageProps= {
|
||||
response: string
|
||||
index: number
|
||||
@@ -126,7 +128,6 @@ const AsyncDashboardInner =({}): JSX.Element => {
|
||||
const handlePromptSubmit = async ({prompt, file, fileType, modelName}: PromptValues, {resetForm}: any): Promise<void> => {
|
||||
|
||||
// send the prompt to be saved
|
||||
|
||||
try{
|
||||
const tempConversations: ConversationPrompt[] = [...conversationDetails, new ConversationPrompt({message: prompt, user_created:true}), new ConversationPrompt({message: '', user_created:false})]
|
||||
|
||||
@@ -135,7 +136,7 @@ const AsyncDashboardInner =({}): JSX.Element => {
|
||||
setConversationDetails(tempConversations)
|
||||
// TODO: add the file here
|
||||
|
||||
sendMessage(prompt, selectedConversation, file, fileType)
|
||||
sendMessage(prompt, selectedConversation, file, fileType, modelName)
|
||||
resetForm();
|
||||
|
||||
|
||||
@@ -205,13 +206,15 @@ return(
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
{/* <CustomSelect
|
||||
name="category"
|
||||
items={models}
|
||||
label="Category"
|
||||
<Select
|
||||
id="modelSelect"
|
||||
label="Model"
|
||||
value={formik.values}
|
||||
required
|
||||
|
||||
/> */}
|
||||
onChange={(e) => {console.log(e.target.value); formik.setFieldValue('modelName', e.target.value)}}
|
||||
>
|
||||
{MODELS.map((model) => <MenuItem value={model} >{model}</MenuItem>)}
|
||||
</Select>
|
||||
<MDButton
|
||||
component="label"
|
||||
startIcon={<AttachFile/>}
|
||||
|
||||
@@ -57,7 +57,7 @@ const ConversationDetail = ({selectedConversation, conversationTitle,conversatio
|
||||
|
||||
useEffect(() => {
|
||||
// now we want to stream the new response
|
||||
const eventSource = new EventSource("http://127.0.0.1:8000/api/streamed_response")
|
||||
const eventSource = new EventSource("http://127.0.0.1:8011/api/streamed_response")
|
||||
try{
|
||||
|
||||
eventSource.onmessage = (event) => {
|
||||
|
||||
@@ -0,0 +1,329 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import MDBox from "../../ui-kit/components/MDBox";
|
||||
import { Box, Button, Card, CardContent, Divider, IconButton, Paper, Switch, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material";
|
||||
import { Document, DocumentType } from "../../data";
|
||||
import { AxiosResponse } from "axios";
|
||||
import { axiosInstance } from "../../../axiosApi";
|
||||
import Header2 from "../../components/Header2/Header2";
|
||||
import PageWrapperLayout from "../../components/PageWrapperLayout/PageWrapperLayout";
|
||||
import Footer from "../../components/Footer/Footer";
|
||||
import MDTypography from "../../ui-kit/components/MDTypography";
|
||||
import FeedbackSubmitCard from "../../components/FeedbackSubmitCard/FeedbackSubmitCard";
|
||||
import { CheckCircle, CloudUpload, DeleteForever, Pending } from "@mui/icons-material";
|
||||
import { Formik } from "formik";
|
||||
import MDButton from "../../ui-kit/components/MDButton";
|
||||
import PaginatedTable from "../../components/PaginatedTable/PaginatedTable";
|
||||
|
||||
type DocumentLineProps = {
|
||||
document: Document,
|
||||
handleDocumentUpdate: (document_id: number, value: string) => void
|
||||
}
|
||||
|
||||
type DocumentTableCardProps = {
|
||||
documents: Document[],
|
||||
setDocuments: React.Dispatch<React.SetStateAction<Document[]>>
|
||||
}
|
||||
|
||||
const DocumentStorageTableRow = ({document, handleDocumentUpdate}: DocumentLineProps): JSX.Element =>
|
||||
{
|
||||
return(
|
||||
<TableRow key={document.id}>
|
||||
<TableCell><MDTypography> {document.name}</MDTypography></TableCell>
|
||||
<TableCell><MDTypography> {document.date_uploaded}</MDTypography></TableCell>
|
||||
|
||||
<TableCell>
|
||||
{ document.processed ? (
|
||||
<IconButton onClick={() => console.log('clicked')}>
|
||||
<CheckCircle color="success" />
|
||||
</IconButton>
|
||||
|
||||
):(
|
||||
<IconButton onClick={() => console.log('clicked')}>
|
||||
<Pending color="disabled" />
|
||||
</IconButton>
|
||||
|
||||
)}
|
||||
|
||||
</TableCell>
|
||||
<TableCell >
|
||||
<Switch checked={document.active} disabled={true} onChange={(event) => handleDocumentUpdate(document.id, event.target.value)} />
|
||||
</TableCell>
|
||||
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
|
||||
const CompanyDocumentStorageTableCard =({documents, setDocuments}: DocumentTableCardProps): JSX.Element => {
|
||||
|
||||
const handleDocumentUpdate = async(document_id: number, value: string): Promise<void> => {
|
||||
console.log(document_id, value)
|
||||
// if(field === 'delete'){
|
||||
// await axiosInstance.delete(`/company_users`, {
|
||||
// data: {'email':email}
|
||||
|
||||
// })
|
||||
// }else {
|
||||
// await axiosInstance.post(`/company_users`, {
|
||||
// 'email':email,
|
||||
// 'field': field,
|
||||
// 'value': value
|
||||
// });
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// // get all of th edata again
|
||||
// try{
|
||||
// const {data, }: AxiosResponse<AccountType[]> = await axiosInstance.get(`/company_users`);
|
||||
// setUsers(data.map((item) => new Account({
|
||||
|
||||
// email: item.email,
|
||||
// first_name: item.first_name,
|
||||
// last_name: item.last_name,
|
||||
// is_company_manager: item.is_company_manager,
|
||||
// has_password: item.has_usable_password,
|
||||
// is_active: item.is_active,
|
||||
// company: undefined
|
||||
// })))
|
||||
|
||||
|
||||
// }catch(error){
|
||||
// console.log(error)
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
async function getUploadedDocuments(){
|
||||
try{
|
||||
const {data, }: AxiosResponse<DocumentType[]> = await axiosInstance.get(`/documents/`);
|
||||
setDocuments(data.map((item) => new Document({
|
||||
|
||||
id: item.id,
|
||||
name: item.file.replace(/^.*[\\\/]/, ''),
|
||||
date_uploaded: item.created.substring(0,10),
|
||||
active: item.active,
|
||||
processed: item.processed,
|
||||
|
||||
})))
|
||||
|
||||
|
||||
}catch(error){
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
getUploadedDocuments();
|
||||
}, [])
|
||||
return (
|
||||
<Card sx={{mt:1}}>
|
||||
|
||||
<CardContent>
|
||||
<MDTypography variant="h3">
|
||||
Your documents in the company workspace
|
||||
|
||||
</MDTypography>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<PaginatedTable
|
||||
data={documents}
|
||||
columns={[
|
||||
{key: 'name', label: 'Name'},
|
||||
{key: 'date_uploaded', label: 'Date Uploaded'},
|
||||
{
|
||||
key: 'processed',
|
||||
label: 'Processed',
|
||||
render: (value) =>( value ? (
|
||||
<IconButton onClick={() => console.log('clicked')}>
|
||||
<CheckCircle color="success" />
|
||||
</IconButton>
|
||||
|
||||
):(
|
||||
<IconButton onClick={() => console.log('clicked')}>
|
||||
<Pending color="disabled" />
|
||||
</IconButton>
|
||||
|
||||
))},
|
||||
{
|
||||
key: 'active',
|
||||
label: 'Active',
|
||||
render: (value) => (
|
||||
<Switch checked={value} disabled={true} />
|
||||
)
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
const UserDocumentStorageTableCard = ({}): JSX.Element => {
|
||||
return(
|
||||
<Card sx={{mt:1}}>
|
||||
|
||||
<CardContent>
|
||||
<MDTypography variant="h3">
|
||||
Your documents in the your personal workspace
|
||||
|
||||
</MDTypography>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<MDTypography>This will become available shortly</MDTypography>
|
||||
{/* <TableContainer component={Paper}>
|
||||
<Table >
|
||||
<TableHead sx={{ display: "table-header-group" }}>
|
||||
<TableRow>
|
||||
<TableCell><MDTypography>Name</MDTypography></TableCell>
|
||||
<TableCell><MDTypography>Date Uploaded</MDTypography></TableCell>
|
||||
<TableCell><MDTypography>Processed</MDTypography></TableCell>
|
||||
<TableCell><MDTypography>Active</MDTypography></TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
|
||||
<MDTypography>This will become available shortly</MDTypography>
|
||||
|
||||
|
||||
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer> */}
|
||||
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
type DocumentSubmitValues = {
|
||||
document: File
|
||||
}
|
||||
|
||||
const DocumentUploadCard = ({}): JSX.Element => {
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
||||
const initialValues = {'file': '',}
|
||||
|
||||
const handleDocumentUpload = async ({document}: DocumentSubmitValues): Promise<void> => {
|
||||
|
||||
console.log(selectedFile)
|
||||
if(selectedFile){
|
||||
|
||||
|
||||
try{
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
const base64File = reader.result?.toString().split(',')[1];
|
||||
axiosInstance.post('/documents/', {
|
||||
file: selectedFile
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
})
|
||||
}
|
||||
reader.readAsDataURL(selectedFile)
|
||||
|
||||
|
||||
// TODO set the documents here
|
||||
}
|
||||
finally{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
// const handleDocumentUpload = async ({email}: InviteValues, {resetForm}: any): Promise<void> => {
|
||||
// try{
|
||||
// await axiosInstance.post('/documents/', {
|
||||
// 'file': file
|
||||
// })
|
||||
// getCompanyUsers()
|
||||
|
||||
// } catch{
|
||||
// // put a message here
|
||||
// }
|
||||
// resetForm();
|
||||
|
||||
// }
|
||||
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.files && event.target.files.length > 0) {
|
||||
setSelectedFile(event.target.files[0]);
|
||||
}
|
||||
};
|
||||
return(
|
||||
<Card sx={{mt:1}}>
|
||||
<CardContent>
|
||||
<MDTypography variant="h3">
|
||||
Upload a Document
|
||||
|
||||
</MDTypography>
|
||||
</CardContent>
|
||||
<Divider />
|
||||
|
||||
<CardContent>
|
||||
<Box sx={{ p: 2 }}>
|
||||
|
||||
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
|
||||
<MDButton
|
||||
component="label"
|
||||
variant="contained"
|
||||
startIcon={<CloudUpload />}
|
||||
>
|
||||
Select File
|
||||
<input type="file" hidden onChange={handleFileChange} />
|
||||
</MDButton>
|
||||
|
||||
{selectedFile && (
|
||||
<>
|
||||
<MDTypography sx={{ ml: 2 }}>{selectedFile.name}</MDTypography>
|
||||
<MDButton
|
||||
variant="contained"
|
||||
color="primary"
|
||||
sx={{ ml: 2 }}
|
||||
onClick={handleDocumentUpload}
|
||||
>
|
||||
Upload
|
||||
</MDButton>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
const DocumentStoragePageInner = ({}): JSX.Element => {
|
||||
const [documents, setDocuments] = useState<Document[]>([]);
|
||||
return(
|
||||
<>
|
||||
<CompanyDocumentStorageTableCard documents={documents} setDocuments={setDocuments}/>
|
||||
<UserDocumentStorageTableCard />
|
||||
<DocumentUploadCard />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const DocumentStoragePage = ({}): JSX.Element => {
|
||||
return (
|
||||
<PageWrapperLayout>
|
||||
<Header2 />
|
||||
<MDBox sx={{mt:12}}>
|
||||
|
||||
</MDBox>
|
||||
<MDBox sx={{ margin: '0 auto', width: '80%', height: '80%', minHeight: '80%', maxHeight: '80%', align:'center'}}>
|
||||
<DocumentStoragePageInner />
|
||||
<Footer />
|
||||
</MDBox>
|
||||
|
||||
</PageWrapperLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export default DocumentStoragePage;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import MDBox from "../../ui-kit/components/MDBox";
|
||||
import { CardContent, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material";
|
||||
import { Card, CardContent, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material";
|
||||
import { Feedback, FeedbackType } from "../../data";
|
||||
import { AxiosResponse } from "axios";
|
||||
import { axiosInstance } from "../../../axiosApi";
|
||||
@@ -9,21 +9,7 @@ import PageWrapperLayout from "../../components/PageWrapperLayout/PageWrapperLay
|
||||
import Footer from "../../components/Footer/Footer";
|
||||
import MDTypography from "../../ui-kit/components/MDTypography";
|
||||
import FeedbackSubmitCard from "../../components/FeedbackSubmitCard/FeedbackSubmitCard";
|
||||
|
||||
type FeedbackLineProps = {
|
||||
feedback: Feedback
|
||||
}
|
||||
|
||||
const FeedbackLine = ({feedback}: FeedbackLineProps): JSX.Element => {
|
||||
return(
|
||||
<TableRow key={feedback.id}>
|
||||
<TableCell><MDTypography>{feedback.title}</MDTypography></TableCell>
|
||||
<TableCell><MDTypography>{feedback.category}</MDTypography></TableCell>
|
||||
<TableCell><MDTypography>{feedback.text}</MDTypography></TableCell>
|
||||
<TableCell><MDTypography>{feedback.status}</MDTypography></TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
import PaginatedTable from "../../components/PaginatedTable/PaginatedTable";
|
||||
|
||||
type FeedbackTableCardProps = {
|
||||
feedbacks: Feedback[],
|
||||
@@ -54,25 +40,27 @@ const FeedbackTableCard =({feedbacks, setFeedbacks}: FeedbackTableCardProps): JS
|
||||
getCompanyUsers();
|
||||
}, [])
|
||||
return(
|
||||
<Card sx={{mt:1}}>
|
||||
|
||||
<CardContent>
|
||||
<TableContainer component={Paper}>
|
||||
<Table >
|
||||
<TableHead sx={{ display: "table-header-group" }}>
|
||||
<TableRow>
|
||||
<TableCell><MDTypography>Title</MDTypography></TableCell>
|
||||
<TableCell><MDTypography>Category</MDTypography></TableCell>
|
||||
<TableCell><MDTypography>Text</MDTypography></TableCell>
|
||||
<TableCell><MDTypography>Status</MDTypography></TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{feedbacks.map((feedback) => <FeedbackLine key={feedback.id} feedback={feedback}/>)}
|
||||
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<MDTypography variant="h3">
|
||||
Feedback
|
||||
|
||||
</MDTypography>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<PaginatedTable
|
||||
data={feedbacks}
|
||||
columns={[
|
||||
{key: 'title', label: 'Title'},
|
||||
{key: 'category', label: 'Category'},
|
||||
{key: 'text', label: 'Text'},
|
||||
{key: 'status', label: 'Status'},
|
||||
]}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,38 @@
|
||||
import { Form, Formik } from 'formik';
|
||||
import React, { Dispatch, PropsWithChildren, SetStateAction, useState } from 'react';
|
||||
import CustomPasswordField from '../../components/CustomPasswordField/CustomPasswordField';
|
||||
import { Button } from '@mui/material';
|
||||
import { axiosInstance } from '../../../axiosApi';
|
||||
import React, { useRef } from 'react';
|
||||
|
||||
import { Card, CardContent, Divider } from '@mui/material';
|
||||
import { axiosInstance, cleanAxiosInstance } from '../../../axiosApi';
|
||||
import CustomToastMessage from '../../components/CustomToastMessage/CustomeToastMessage';
|
||||
import PageWrapperLayout from '../../components/PageWrapperLayout/PageWrapperLayout';
|
||||
import MDBox from '../../ui-kit/components/MDBox';
|
||||
import background from '../../../bg.jpeg'
|
||||
import { Col, Row } from 'react-bootstrap';
|
||||
import MDTypography from '../../ui-kit/components/MDTypography';
|
||||
import CustomTextField from '../../components/CustomTextField/CustomTextField';
|
||||
import MDButton from '../../ui-kit/components/MDButton';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import ReCAPTCHA from 'react-google-recaptcha';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export type PasswordResetValues = {
|
||||
password1: string;
|
||||
password2: string;
|
||||
};
|
||||
|
||||
export type EmailPasswordResetValues = {
|
||||
email: string;
|
||||
}
|
||||
|
||||
|
||||
const PasswordReset = ({}): JSX.Element => {
|
||||
const navigate = useNavigate();
|
||||
const recaptchaRef = useRef<ReCAPTCHA>(null);
|
||||
|
||||
const handlePasswordReset = ({password1, password2}: PasswordResetValues): void => {
|
||||
try{
|
||||
// verify
|
||||
@@ -26,74 +47,166 @@ const PasswordReset = ({}): JSX.Element => {
|
||||
<CustomToastMessage message={error as string} />
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const handlePasswordResetEmail = ({email}: EmailPasswordResetValues): void => {
|
||||
if(recaptchaRef.current){
|
||||
const token = recaptchaRef.current.getValue();
|
||||
if (!token) {
|
||||
|
||||
try{
|
||||
cleanAxiosInstance.post('user/reset_password',
|
||||
{
|
||||
'email': email,
|
||||
'recaptchaToken': token
|
||||
}
|
||||
);
|
||||
// navigate to another page now
|
||||
navigate('/password_reset_confirmation')
|
||||
}catch(error){
|
||||
console.log('error')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='main-content' style={{'height': '100%', minHeight: '100vh', display: 'flex', flexDirection: 'column'}}>
|
||||
<div className='container my-auto'>
|
||||
<div className='row'>
|
||||
<div className='col -lg-4 col-md-8 col-12 mx-auto'>
|
||||
<div className='card z-index-0 fadeIn3 fadeInBottom'>
|
||||
<div className='card-header p-0 position-relative mt-n4 mx-3 z-index-2'>
|
||||
<div className='bg-gradient-dark shadow-dark border-radius-lg py-3 pe-1'>
|
||||
<h4 className='text-white font-weight-bold text-center'>Password Reset</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div className='card-body text-center'>
|
||||
<PageWrapperLayout>
|
||||
<MDBox sx={{'height': '100%', minHeight: '100vh', display: 'flex', flexDirection: 'column', backgroundImage: `url(${background})`,backgroundSize: "cover",
|
||||
backgroundRepeat: "no-repeat",}}>
|
||||
|
||||
<MDBox sx={{ margin: '0 auto', width: '80%', height: '80%', minHeight: '80%', maxHeight: '80%', align:'center'}}>
|
||||
<Row>
|
||||
<Col className='col -lg-4 col-md-8 col-12 mx-auto'>
|
||||
<Card sx={{mt:30}} >
|
||||
<CardContent>
|
||||
<MDTypography variant="h3">
|
||||
Reset Password
|
||||
</MDTypography>
|
||||
</CardContent>
|
||||
<Divider />
|
||||
<Formik
|
||||
initialValues={{
|
||||
password1: '',
|
||||
password2: '',
|
||||
email: '',
|
||||
}}
|
||||
onSubmit={handlePasswordReset}>
|
||||
onSubmit={handlePasswordResetEmail}
|
||||
|
||||
>
|
||||
{(formik) => (
|
||||
<Form>
|
||||
<div className='row'>
|
||||
<div className='col'>
|
||||
<CustomPasswordField
|
||||
label='Password'
|
||||
name="password1"
|
||||
changeHandler={(e) => formik.setFieldValue('password1', e.target.value)} />
|
||||
<CustomTextField
|
||||
label='Email'
|
||||
name="email"
|
||||
changeHandler={(e) => formik.setFieldValue('email', e.target.value)} isMultline={false}/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div className='row'>
|
||||
<div className='col'>
|
||||
<CustomPasswordField
|
||||
label='Confirm Password'
|
||||
name="password2"
|
||||
changeHandler={(e) => formik.setFieldValue('password2', e.target.value)} />
|
||||
<ReCAPTCHA
|
||||
ref={recaptchaRef}
|
||||
sitekey = "6LfENu4qAAAAAFtPejcrP3dwBDxcRPjqi7RhytJJ"
|
||||
size="invisible"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div className='row'>
|
||||
<div className='col'>
|
||||
<Button
|
||||
<MDButton
|
||||
type={'submit'}
|
||||
disabled={ formik.isSubmitting}
|
||||
// type={'submit'}
|
||||
// loading={formik.isSubmitting}
|
||||
// disabled={
|
||||
// !formik.isValid || !formik.dirty || formik.isSubmitting
|
||||
// }
|
||||
fullWidth
|
||||
>
|
||||
Reset Password
|
||||
</Button>
|
||||
<MDTypography
|
||||
as="h6">
|
||||
Rest Password
|
||||
|
||||
</MDTypography>
|
||||
|
||||
</MDButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</Form>
|
||||
)}
|
||||
|
||||
</Formik>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</MDBox>
|
||||
</MDBox>
|
||||
|
||||
</PageWrapperLayout>
|
||||
// <div className='main-content' style={{'height': '100%', minHeight: '100vh', display: 'flex', flexDirection: 'column'}}>
|
||||
// <div className='container my-auto'>
|
||||
// <div className='row'>
|
||||
// <div className='col -lg-4 col-md-8 col-12 mx-auto'>
|
||||
// <div className='card z-index-0 fadeIn3 fadeInBottom'>
|
||||
// <div className='card-header p-0 position-relative mt-n4 mx-3 z-index-2'>
|
||||
// <div className='bg-gradient-dark shadow-dark border-radius-lg py-3 pe-1'>
|
||||
// <h4 className='text-white font-weight-bold text-center'>Password Reset</h4>
|
||||
// </div>
|
||||
// </div>
|
||||
// <div className='card-body text-center'>
|
||||
// <Formik
|
||||
// initialValues={{
|
||||
// password1: '',
|
||||
// password2: '',
|
||||
// }}
|
||||
// onSubmit={handlePasswordReset}>
|
||||
// {(formik) => (
|
||||
// <Form>
|
||||
// <div className='row'>
|
||||
// <div className='col'>
|
||||
// <CustomPasswordField
|
||||
// label='Password'
|
||||
// name="password1"
|
||||
// changeHandler={(e) => formik.setFieldValue('password1', e.target.value)} />
|
||||
|
||||
// </div>
|
||||
// </div>
|
||||
// <div className='row'>
|
||||
// <div className='col'>
|
||||
// <CustomPasswordField
|
||||
// label='Confirm Password'
|
||||
// name="password2"
|
||||
// changeHandler={(e) => formik.setFieldValue('password2', e.target.value)} />
|
||||
|
||||
// </div>
|
||||
// </div>
|
||||
// <div className='row'>
|
||||
// <div className='col'>
|
||||
// <Button
|
||||
// type={'submit'}
|
||||
// disabled={ formik.isSubmitting}
|
||||
// // type={'submit'}
|
||||
// // loading={formik.isSubmitting}
|
||||
// // disabled={
|
||||
// // !formik.isValid || !formik.dirty || formik.isSubmitting
|
||||
// // }
|
||||
// >
|
||||
// Reset Password
|
||||
// </Button>
|
||||
|
||||
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// </Form>
|
||||
// )}
|
||||
|
||||
// </Formik>
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { Form, Formik } from 'formik';
|
||||
import React, { Dispatch, PropsWithChildren, SetStateAction, useState } from 'react';
|
||||
import CustomPasswordField from '../../components/CustomPasswordField/CustomPasswordField';
|
||||
import { Button, Card, CardContent, Divider } from '@mui/material';
|
||||
import { axiosInstance } from '../../../axiosApi';
|
||||
import CustomToastMessage from '../../components/CustomToastMessage/CustomeToastMessage';
|
||||
import PageWrapperLayout from '../../components/PageWrapperLayout/PageWrapperLayout';
|
||||
import MDBox from '../../ui-kit/components/MDBox';
|
||||
import background from '../../../bg.jpeg'
|
||||
import { Col, Row } from 'react-bootstrap';
|
||||
import MDTypography from '../../ui-kit/components/MDTypography';
|
||||
import { valuesIn } from 'lodash';
|
||||
import CustomTextField from '../../components/CustomTextField/CustomTextField';
|
||||
import MDButton from '../../ui-kit/components/MDButton';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
export type PasswordResetValues = {
|
||||
password1: string;
|
||||
password2: string;
|
||||
};
|
||||
|
||||
export type EmailPasswordResetValues = {
|
||||
email: string;
|
||||
}
|
||||
|
||||
|
||||
const PasswordResetConfirmation = ({}): JSX.Element => {
|
||||
|
||||
|
||||
return (
|
||||
<PageWrapperLayout>
|
||||
<MDBox sx={{'height': '100%', minHeight: '100vh', display: 'flex', flexDirection: 'column', backgroundImage: `url(${background})`,backgroundSize: "cover",
|
||||
backgroundRepeat: "no-repeat",}}>
|
||||
|
||||
<MDBox sx={{ margin: '0 auto', width: '80%', height: '80%', minHeight: '80%', maxHeight: '80%', align:'center'}}>
|
||||
<Row>
|
||||
<Col className='col -lg-4 col-md-8 col-12 mx-auto'>
|
||||
<Card sx={{mt:30}} >
|
||||
<CardContent>
|
||||
<MDTypography variant="h3">
|
||||
Reset Password Confirmation
|
||||
</MDTypography>
|
||||
</CardContent>
|
||||
<Divider />
|
||||
<MDTypography>
|
||||
Check your email for a link to set your password!
|
||||
</MDTypography>
|
||||
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</MDBox>
|
||||
</MDBox>
|
||||
|
||||
</PageWrapperLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default PasswordResetConfirmation;
|
||||
@@ -91,6 +91,8 @@ const SignIn = ({}): JSX.Element => {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<PageWrapperLayout>
|
||||
<MDBox sx={{'height': '100%', minHeight: '100vh', display: 'flex', flexDirection: 'column', backgroundImage: `url(${background})`,backgroundSize: "cover",
|
||||
@@ -140,15 +142,34 @@ const SignIn = ({}): JSX.Element => {
|
||||
</div>
|
||||
</div>
|
||||
<div className='row'>
|
||||
<div className='col'>
|
||||
<div className='col-6'>
|
||||
<MDButton
|
||||
type={'submit'}
|
||||
fullWidth
|
||||
>
|
||||
<MDTypography
|
||||
as="h6">
|
||||
Sign In
|
||||
|
||||
</MDTypography>
|
||||
|
||||
</MDButton>
|
||||
|
||||
|
||||
</div>
|
||||
{/* <div className='col-6'>
|
||||
<MDButton
|
||||
fullWidth
|
||||
onClick={() => navigate('/password_reset')}
|
||||
>
|
||||
<MDTypography
|
||||
color="error"
|
||||
as="h6"
|
||||
>
|
||||
Reset Password
|
||||
|
||||
</MDTypography>
|
||||
|
||||
</MDButton>
|
||||
</div> */}
|
||||
</div>
|
||||
|
||||
</Form>
|
||||
|
||||
Reference in New Issue
Block a user