Forced merge: merge ai-dev into master
This commit is contained in:
commit
cb57673c43
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,3 +1,8 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
*/node_modules/
|
*/node_modules/
|
||||||
*/build/
|
*/build/
|
||||||
|
|
||||||
|
**/node_modules/
|
||||||
|
**/build/
|
||||||
|
.DS_Store
|
||||||
|
.env
|
||||||
File diff suppressed because one or more lines are too long
@ -60,7 +60,7 @@ const config = {
|
|||||||
? 'https://flatlogic.com/projects'
|
? 'https://flatlogic.com/projects'
|
||||||
: 'http://localhost:3000/projects',
|
: 'http://localhost:3000/projects',
|
||||||
|
|
||||||
gpt_key: process.env.GPT_KEY || '',
|
gpt_key: process.env.GPT_KEY || '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',
|
||||||
};
|
};
|
||||||
|
|
||||||
config.pexelsKey = process.env.PEXELS_KEY || '';
|
config.pexelsKey = process.env.PEXELS_KEY || '';
|
||||||
|
|||||||
1
frontend/json/runtimeError.json
Normal file
1
frontend/json/runtimeError.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
@ -4,6 +4,9 @@
|
|||||||
"pageTitle": "Dashboard",
|
"pageTitle": "Dashboard",
|
||||||
"overview": "Übersicht",
|
"overview": "Übersicht",
|
||||||
"loadingWidgets": "Widgets werden geladen...",
|
"loadingWidgets": "Widgets werden geladen...",
|
||||||
|
"chatPlaceholder": "Frag etwas...",
|
||||||
|
"chatButton": "Frag GPT",
|
||||||
|
|
||||||
"loading": "Laden..."
|
"loading": "Laden..."
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
|
|||||||
@ -4,6 +4,9 @@
|
|||||||
"pageTitle": "Dashboard",
|
"pageTitle": "Dashboard",
|
||||||
"overview": "Overview",
|
"overview": "Overview",
|
||||||
"loadingWidgets": "Loading widgets...",
|
"loadingWidgets": "Loading widgets...",
|
||||||
|
"chatPlaceholder": "Ask something...",
|
||||||
|
"chatButton": "Ask GPT",
|
||||||
|
|
||||||
"loading": "Loading..."
|
"loading": "Loading..."
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import * as icon from '@mdi/js';
|
import * as icon from '@mdi/js';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import type { ReactElement } from 'react';
|
import type { ReactElement } from 'react';
|
||||||
import LayoutAuthenticated from '../layouts/Authenticated';
|
import LayoutAuthenticated from '../layouts/Authenticated';
|
||||||
@ -15,12 +15,21 @@ import { hasPermission } from '../helpers/userPermissions';
|
|||||||
import { fetchWidgets } from '../stores/roles/rolesSlice';
|
import { fetchWidgets } from '../stores/roles/rolesSlice';
|
||||||
import { WidgetCreator } from '../components/WidgetCreator/WidgetCreator';
|
import { WidgetCreator } from '../components/WidgetCreator/WidgetCreator';
|
||||||
import { SmartWidget } from '../components/SmartWidget/SmartWidget';
|
import { SmartWidget } from '../components/SmartWidget/SmartWidget';
|
||||||
|
import { askGpt, clearResponse } from '../stores/openAiSlice';
|
||||||
|
|
||||||
import { useAppDispatch, useAppSelector } from '../stores/hooks';
|
import { useAppDispatch, useAppSelector } from '../stores/hooks';
|
||||||
const Dashboard = () => {
|
const Dashboard = () => {
|
||||||
const { t } = useTranslation('common');
|
const { t } = useTranslation('common');
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const iconsColor = useAppSelector((state) => state.style.iconsColor);
|
const iconsColor = useAppSelector((state) => state.style.iconsColor);
|
||||||
|
const [userPrompt, setUserPrompt] = useState('');
|
||||||
|
const { gptResponse: chatResponse, isAskingQuestion: chatLoading, errorMessage } = useAppSelector(state => state.openAi);
|
||||||
|
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (userPrompt.trim()) {
|
||||||
|
dispatch(askGpt(userPrompt));
|
||||||
|
}
|
||||||
|
};
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
const corners = useAppSelector((state) => state.style.corners);
|
||||||
const cardsStyle = useAppSelector((state) => state.style.cardsStyle);
|
const cardsStyle = useAppSelector((state) => state.style.cardsStyle);
|
||||||
|
|
||||||
@ -38,12 +47,23 @@ const Dashboard = () => {
|
|||||||
const [students, setStudents] = React.useState(loadingMessage);
|
const [students, setStudents] = React.useState(loadingMessage);
|
||||||
const [roles, setRoles] = React.useState(loadingMessage);
|
const [roles, setRoles] = React.useState(loadingMessage);
|
||||||
const [permissions, setPermissions] = React.useState(loadingMessage);
|
const [permissions, setPermissions] = React.useState(loadingMessage);
|
||||||
|
const promptForGreeting = "Write a short ironic greeting for a dashboard page, in 3-5 words.";
|
||||||
|
const isAsking = useAppSelector((state) => state.openAi?.isAskingQuestion ?? false);
|
||||||
|
const gptResp = useAppSelector((state) => state.openAi?.gptResponse);
|
||||||
|
const [greeting, setGreeting] = useState<string>('');
|
||||||
|
useEffect(() => {
|
||||||
|
if (!greeting && gptResp) {
|
||||||
|
setGreeting(gptResp);
|
||||||
|
dispatch(clearResponse());
|
||||||
|
}
|
||||||
|
}, [gptResp, greeting, dispatch]);
|
||||||
|
useEffect(() => { dispatch(askGpt(promptForGreeting)); }, [dispatch]);
|
||||||
|
|
||||||
const [widgetsRole, setWidgetsRole] = React.useState({
|
const [widgetsRole, setWidgetsRole] = React.useState({
|
||||||
role: { value: '', label: '' },
|
role: { value: '', label: '' },
|
||||||
});
|
});
|
||||||
const { currentUser } = useAppSelector((state) => state.auth);
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
const { isFetchingQuery } = useAppSelector((state) => state.openAi);
|
const isFetchingQuery = useAppSelector((state) => state.openAi?.isFetchingQuery ?? false);
|
||||||
|
|
||||||
const { rolesWidgets, loading } = useAppSelector((state) => state.roles);
|
const { rolesWidgets, loading } = useAppSelector((state) => state.roles);
|
||||||
|
|
||||||
@ -119,13 +139,15 @@ const Dashboard = () => {
|
|||||||
)}
|
)}
|
||||||
</title>
|
</title>
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
<SectionMain>
|
<SectionMain>
|
||||||
<SectionTitleLineWithButton
|
<SectionTitleLineWithButton
|
||||||
icon={icon.mdiChartTimelineVariant}
|
icon={icon.mdiChartTimelineVariant}
|
||||||
title={t('pages.dashboard.overview', { defaultValue: 'Overview' })}
|
title={greeting || t('pages.dashboard.overview', { defaultValue: 'Overview' })}
|
||||||
main
|
main
|
||||||
>
|
>
|
||||||
{''}
|
{''}
|
||||||
|
|
||||||
</SectionTitleLineWithButton>
|
</SectionTitleLineWithButton>
|
||||||
|
|
||||||
{hasPermission(currentUser, 'CREATE_ROLES') && (
|
{hasPermission(currentUser, 'CREATE_ROLES') && (
|
||||||
@ -177,6 +199,23 @@ const Dashboard = () => {
|
|||||||
|
|
||||||
{!!rolesWidgets.length && <hr className='my-6 ' />}
|
{!!rolesWidgets.length && <hr className='my-6 ' />}
|
||||||
|
|
||||||
|
{/* ChatGPT Input Form - relocated above count widgets */}
|
||||||
|
<form className='max-w-xs w-full mx-auto mb-4' onSubmit={handleSubmit}>
|
||||||
|
<textarea
|
||||||
|
rows={3}
|
||||||
|
value={userPrompt}
|
||||||
|
onChange={e => setUserPrompt(e.target.value)}
|
||||||
|
className='w-full p-2 border rounded'
|
||||||
|
placeholder={t('pages.dashboard.chatPlaceholder', { defaultValue: 'Ask something...' })}
|
||||||
|
/>
|
||||||
|
<button type='submit' className='mt-1 px-3 py-1 bg-blue-500 text-white rounded'>
|
||||||
|
{t('pages.dashboard.chatButton', { defaultValue: 'Ask GPT' })}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
{chatLoading && <p className='text-gray-500'>{loadingMessage}</p>}
|
||||||
|
{errorMessage && <p className='text-red-500'>{errorMessage}</p>}
|
||||||
|
{chatResponse && <p className='mt-2 p-2 bg-gray-100 rounded'>{chatResponse}</p>}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
id='dashboard'
|
id='dashboard'
|
||||||
className='grid grid-cols-1 gap-6 lg:grid-cols-3 mb-6'
|
className='grid grid-cols-1 gap-6 lg:grid-cols-3 mb-6'
|
||||||
@ -196,6 +235,8 @@ const Dashboard = () => {
|
|||||||
<div className='text-3xl leading-tight font-semibold'>
|
<div className='text-3xl leading-tight font-semibold'>
|
||||||
{users}
|
{users}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<BaseIcon
|
<BaseIcon
|
||||||
|
|||||||
@ -73,6 +73,11 @@ export const openAiSlice = createSlice({
|
|||||||
setErrorNotification: (state, action) => {
|
setErrorNotification: (state, action) => {
|
||||||
fulfilledNotify(state, action.payload, 'error');
|
fulfilledNotify(state, action.payload, 'error');
|
||||||
},
|
},
|
||||||
|
clearResponse: (state) => {
|
||||||
|
state.gptResponse = null;
|
||||||
|
state.isAskingQuestion = false;
|
||||||
|
state.errorMessage = '';
|
||||||
|
},
|
||||||
},
|
},
|
||||||
extraReducers: (builder) => {
|
extraReducers: (builder) => {
|
||||||
builder.addCase(aiPrompt.pending, (state) => {
|
builder.addCase(aiPrompt.pending, (state) => {
|
||||||
@ -111,6 +116,6 @@ export const openAiSlice = createSlice({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Action creators are generated for each case reducer function
|
// Action creators are generated for each case reducer function
|
||||||
export const { resetNotify, setErrorNotification } = openAiSlice.actions;
|
export const { resetNotify, setErrorNotification, clearResponse } = openAiSlice.actions;
|
||||||
|
|
||||||
export default openAiSlice.reducer;
|
export default openAiSlice.reducer;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user