diff --git a/.prettierrc b/.prettierrc index 30e905d..41c0fcd 100644 --- a/.prettierrc +++ b/.prettierrc @@ -4,5 +4,6 @@ "singleQuote": false, "tabWidth": 2, "useTabs": false, - "trailingComma": "none" + "trailingComma": "none", + "plugins": ["prettier-plugin-tailwindcss"] } diff --git a/package-lock.json b/package-lock.json index 7134cbd..a634d13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,6 +62,7 @@ "husky": "8.0.0", "lint-staged": "13.2.3", "prettier": "3.0.0", + "prettier-plugin-tailwindcss": "^0.5.4", "tailwindcss": "3.3.3", "typescript": "4.9.5" } @@ -13360,6 +13361,78 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prettier-plugin-tailwindcss": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.4.tgz", + "integrity": "sha512-QZzzB1bID6qPsKHTeA9qPo1APmmxfFrA5DD3LQ+vbTmAnY40eJI7t9Q1ocqel2EKMWNPLJqdTDWZj1hKYgqSgg==", + "dev": true, + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-pug": "*", + "@shopify/prettier-plugin-liquid": "*", + "@shufo/prettier-plugin-blade": "*", + "@trivago/prettier-plugin-sort-imports": "*", + "prettier": "^3.0", + "prettier-plugin-astro": "*", + "prettier-plugin-css-order": "*", + "prettier-plugin-import-sort": "*", + "prettier-plugin-jsdoc": "*", + "prettier-plugin-organize-attributes": "*", + "prettier-plugin-organize-imports": "*", + "prettier-plugin-style-order": "*", + "prettier-plugin-svelte": "*" + }, + "peerDependenciesMeta": { + "@ianvs/prettier-plugin-sort-imports": { + "optional": true + }, + "@prettier/plugin-pug": { + "optional": true + }, + "@shopify/prettier-plugin-liquid": { + "optional": true + }, + "@shufo/prettier-plugin-blade": { + "optional": true + }, + "@trivago/prettier-plugin-sort-imports": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + }, + "prettier-plugin-css-order": { + "optional": true + }, + "prettier-plugin-import-sort": { + "optional": true + }, + "prettier-plugin-jsdoc": { + "optional": true + }, + "prettier-plugin-marko": { + "optional": true + }, + "prettier-plugin-organize-attributes": { + "optional": true + }, + "prettier-plugin-organize-imports": { + "optional": true + }, + "prettier-plugin-style-order": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + }, + "prettier-plugin-twig-melody": { + "optional": true + } + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -27649,6 +27722,13 @@ "integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", "dev": true }, + "prettier-plugin-tailwindcss": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.4.tgz", + "integrity": "sha512-QZzzB1bID6qPsKHTeA9qPo1APmmxfFrA5DD3LQ+vbTmAnY40eJI7t9Q1ocqel2EKMWNPLJqdTDWZj1hKYgqSgg==", + "dev": true, + "requires": {} + }, "pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", diff --git a/package.json b/package.json index 770d7ae..82adcc4 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "husky": "8.0.0", "lint-staged": "13.2.3", "prettier": "3.0.0", + "prettier-plugin-tailwindcss": "^0.5.4", "tailwindcss": "3.3.3", "typescript": "4.9.5" } diff --git a/specs/V1.2.json b/specs/V1.2.0.json similarity index 100% rename from specs/V1.2.json rename to specs/V1.2.0.json diff --git a/specs/V1.2.7.json b/specs/V1.2.7.json new file mode 100644 index 0000000..6de3052 --- /dev/null +++ b/specs/V1.2.7.json @@ -0,0 +1,1067 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Genesi AI Plugin API", + "description": "API set to get and manage Plugins. routes are intended to be accessed on subdomains in the format {pluginId}.Genesi.AI.", + "version": "1.2.7" + }, + "paths": { + "/api/Chat": { + "post": { + "tags": [ + "Chat" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Chat" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/Chat" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/Chat" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ChatResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChatResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ChatResponse" + } + } + } + } + } + } + }, + "/api/Payments/intent": { + "get": { + "tags": [ + "Payments" + ], + "responses": { + "200": { + "description": "Success" + } + } + } + }, + "/api/Payments/unsubscribe": { + "post": { + "tags": [ + "Payments" + ], + "responses": { + "200": { + "description": "Success" + } + } + } + }, + "/api/Payments/webhook": { + "post": { + "tags": [ + "Payments" + ], + "responses": { + "200": { + "description": "Success" + } + } + } + }, + "/api/plugins": { + "post": { + "tags": [ + "Plugin" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PluginCreateRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/PluginCreateRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/PluginCreateRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/Plugin" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Plugin" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/Plugin" + } + } + } + } + } + }, + "get": { + "tags": [ + "Plugin" + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/PluginsResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/PluginsResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/PluginsResponse" + } + } + } + } + } + } + }, + "/api/plugins/{pluginId}": { + "get": { + "tags": [ + "Plugin" + ], + "parameters": [ + { + "name": "pluginId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/Plugin" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Plugin" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/Plugin" + } + } + } + } + } + }, + "put": { + "tags": [ + "Plugin" + ], + "parameters": [ + { + "name": "pluginId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PluginUpdateRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/PluginUpdateRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/PluginUpdateRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Success" + } + } + }, + "delete": { + "tags": [ + "Plugin" + ], + "parameters": [ + { + "name": "pluginId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success" + } + } + } + }, + "/api/plugins/{pluginId}/sections": { + "post": { + "tags": [ + "Plugin" + ], + "parameters": [ + { + "name": "pluginId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SectionCreateRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/SectionCreateRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/SectionCreateRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/Section" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Section" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/Section" + } + } + } + } + } + }, + "get": { + "tags": [ + "Plugin" + ], + "parameters": [ + { + "name": "pluginId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Section" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Section" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Section" + } + } + } + } + } + } + } + }, + "/api/plugins/{pluginId}/sections/{sectionId}": { + "get": { + "tags": [ + "Plugin" + ], + "parameters": [ + { + "name": "pluginId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "sectionId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/Section" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Section" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/Section" + } + } + } + } + } + }, + "put": { + "tags": [ + "Plugin" + ], + "parameters": [ + { + "name": "sectionId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "pluginId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SectionUpdateRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/SectionUpdateRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/SectionUpdateRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Success" + } + } + }, + "delete": { + "tags": [ + "Plugin" + ], + "parameters": [ + { + "name": "sectionId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "pluginId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success" + } + } + } + }, + "/.well-known/ai-plugin.json": { + "get": { + "tags": [ + "PublicPlugin" + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/AiPluginManifest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/AiPluginManifest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/AiPluginManifest" + } + } + } + } + } + } + }, + "/openapi.json": { + "get": { + "tags": [ + "PublicPlugin" + ], + "responses": { + "200": { + "description": "Success" + } + } + } + }, + "/contact": { + "post": { + "tags": [ + "PublicPlugin" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContactFormRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ContactFormRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/ContactFormRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Success" + } + } + } + }, + "/{sectionName}": { + "get": { + "tags": [ + "PublicPlugin" + ], + "parameters": [ + { + "name": "sectionName", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/Section" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Section" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/Section" + } + } + } + } + } + } + }, + "/api/User": { + "get": { + "tags": [ + "User" + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/UserInfo" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserInfo" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UserInfo" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "AiPluginManifest": { + "type": "object", + "properties": { + "schema_version": { + "type": "string", + "nullable": true + }, + "name_for_human": { + "maxLength": 20, + "type": "string", + "nullable": true + }, + "name_for_model": { + "maxLength": 50, + "type": "string", + "nullable": true + }, + "description_for_human": { + "maxLength": 100, + "type": "string", + "nullable": true + }, + "description_for_model": { + "maxLength": 8000, + "type": "string", + "nullable": true + }, + "auth": { + "$ref": "#/components/schemas/Auth" + }, + "api": { + "$ref": "#/components/schemas/Api" + }, + "logo_url": { + "type": "string", + "nullable": true + }, + "contact_email": { + "type": "string", + "nullable": true + }, + "legal_info_url": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "Api": { + "type": "object", + "properties": { + "type": { + "type": "string", + "nullable": true + }, + "url": { + "type": "string", + "nullable": true + }, + "is_user_authenticated": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "Auth": { + "type": "object", + "properties": { + "type": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "Chat": { + "type": "object", + "properties": { + "aiPlugin": { + "$ref": "#/components/schemas/Plugin" + }, + "messages": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Message" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "ChatData": { + "type": "object", + "properties": { + "maxMessagesLast24H": { + "type": "integer", + "format": "int32" + }, + "messagesLast24H": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "ChatResponse": { + "type": "object", + "properties": { + "message": { + "$ref": "#/components/schemas/Message" + }, + "chatData": { + "$ref": "#/components/schemas/ChatData" + } + }, + "additionalProperties": false + }, + "ContactFormRequest": { + "required": [ + "email", + "message", + "name" + ], + "type": "object", + "properties": { + "name": { + "minLength": 1, + "type": "string" + }, + "email": { + "minLength": 1, + "type": "string", + "format": "email" + }, + "message": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "Message": { + "type": "object", + "properties": { + "role": { + "type": "string", + "nullable": true + }, + "content": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "Plugin": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "isActive": { + "type": "boolean" + }, + "userId": { + "type": "string", + "nullable": true + }, + "sections": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Section" + }, + "nullable": true + }, + "nameForHuman": { + "maxLength": 20, + "type": "string", + "nullable": true + }, + "nameForModel": { + "maxLength": 50, + "type": "string", + "nullable": true + }, + "descriptionForHuman": { + "maxLength": 100, + "type": "string", + "nullable": true + }, + "descriptionForModel": { + "maxLength": 8000, + "type": "string", + "nullable": true + }, + "logoUrl": { + "type": "string", + "nullable": true + }, + "contactEmail": { + "type": "string", + "nullable": true + }, + "legalInfoUrl": { + "type": "string", + "nullable": true + }, + "creationDateTime": { + "type": "string", + "format": "date-time" + }, + "isDeleted": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "PluginCreateRequest": { + "type": "object", + "properties": { + "nameForHuman": { + "maxLength": 20, + "minLength": 0, + "type": "string", + "nullable": true + }, + "nameForModel": { + "maxLength": 50, + "minLength": 0, + "type": "string", + "nullable": true + }, + "descriptionForHuman": { + "maxLength": 100, + "minLength": 0, + "type": "string", + "nullable": true + }, + "descriptionForModel": { + "maxLength": 8000, + "minLength": 0, + "type": "string", + "nullable": true + }, + "logoUrl": { + "type": "string", + "nullable": true + }, + "contactEmail": { + "type": "string", + "nullable": true + }, + "legalInfoUrl": { + "type": "string", + "nullable": true + }, + "sections": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SectionCreateRequest" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PluginUpdateRequest": { + "type": "object", + "properties": { + "nameForHuman": { + "maxLength": 20, + "type": "string", + "nullable": true + }, + "nameForModel": { + "maxLength": 50, + "type": "string", + "nullable": true + }, + "descriptionForHuman": { + "maxLength": 100, + "type": "string", + "nullable": true + }, + "descriptionForModel": { + "maxLength": 8000, + "type": "string", + "nullable": true + }, + "logoUrl": { + "type": "string", + "nullable": true + }, + "contactEmail": { + "type": "string", + "nullable": true + }, + "legalInfoUrl": { + "type": "string", + "nullable": true + }, + "sections": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SectionCreateRequest" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PluginsResponse": { + "type": "object", + "properties": { + "pluginsCount": { + "type": "integer", + "format": "int32" + }, + "maxPlugins": { + "type": "integer", + "format": "int32" + }, + "plugins": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Plugin" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "Section": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "pluginId": { + "type": "string", + "format": "uuid" + }, + "name": { + "maxLength": 50, + "type": "string", + "nullable": true + }, + "description": { + "maxLength": 200, + "type": "string", + "nullable": true + }, + "content": { + "maxLength": 100000, + "type": "string", + "nullable": true + }, + "isDeleted": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "SectionCreateRequest": { + "type": "object", + "properties": { + "name": { + "maxLength": 200, + "type": "string", + "nullable": true + }, + "description": { + "maxLength": 200, + "type": "string", + "nullable": true + }, + "content": { + "maxLength": 100000, + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "SectionUpdateRequest": { + "type": "object", + "properties": { + "name": { + "maxLength": 200, + "type": "string", + "nullable": true + }, + "description": { + "maxLength": 200, + "type": "string", + "nullable": true + }, + "content": { + "maxLength": 100000, + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "UserInfo": { + "type": "object", + "properties": { + "isPremium": { + "type": "boolean" + }, + "chatData": { + "$ref": "#/components/schemas/ChatData" + } + }, + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/src/AppRouter.tsx b/src/AppRouter.tsx index 3f8a536..0e874ad 100644 --- a/src/AppRouter.tsx +++ b/src/AppRouter.tsx @@ -64,7 +64,7 @@ const AppRouter: React.FC = () => { path={plugin} element={ - + diff --git a/src/Tailwind.css b/src/Tailwind.css index 7c05d4d..7a58c64 100644 --- a/src/Tailwind.css +++ b/src/Tailwind.css @@ -1,2 +1,6 @@ @tailwind components; @tailwind utilities; + +.max-h-textarea textarea { + max-height: 128px !important; +} diff --git a/src/apis/api.ts b/src/apis/api.ts index 9d06f01..2a4c1ad 100644 --- a/src/apis/api.ts +++ b/src/apis/api.ts @@ -4,7 +4,7 @@ * Genesi AI Plugin API * API set to get and manage Plugins. routes are intended to be accessed on subdomains in the format {pluginId}.Genesi.AI. * - * The version of the OpenAPI document: 1.2.6 + * The version of the OpenAPI document: 1.2.7 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -128,6 +128,63 @@ export interface Auth { */ 'type'?: string | null; } +/** + * + * @export + * @interface Chat + */ +export interface Chat { + /** + * + * @type {Plugin} + * @memberof Chat + */ + 'aiPlugin'?: Plugin; + /** + * + * @type {Array} + * @memberof Chat + */ + 'messages'?: Array | null; +} +/** + * + * @export + * @interface ChatData + */ +export interface ChatData { + /** + * + * @type {number} + * @memberof ChatData + */ + 'maxMessagesLast24H'?: number; + /** + * + * @type {number} + * @memberof ChatData + */ + 'messagesLast24H'?: number; +} +/** + * + * @export + * @interface ChatResponse + */ +export interface ChatResponse { + /** + * + * @type {Message} + * @memberof ChatResponse + */ + 'message'?: Message; + /** + * + * @type {ChatData} + * @memberof ChatResponse + */ + 'chatData'?: ChatData; +} /** * * @export @@ -153,6 +210,25 @@ export interface ContactFormRequest { */ 'message': string; } +/** + * + * @export + * @interface Message + */ +export interface Message { + /** + * + * @type {string} + * @memberof Message + */ + 'role'?: string | null; + /** + * + * @type {string} + * @memberof Message + */ + 'content'?: string | null; +} /** * * @export @@ -478,8 +554,115 @@ export interface UserInfo { * @memberof UserInfo */ 'isPremium'?: boolean; + /** + * + * @type {ChatData} + * @memberof UserInfo + */ + 'chatData'?: ChatData; } +/** + * ChatApi - axios parameter creator + * @export + */ +export const ChatApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * + * @param {Chat} [chat] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + apiChatPost: async (chat?: Chat, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/Chat`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(chat, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * ChatApi - functional programming interface + * @export + */ +export const ChatApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = ChatApiAxiosParamCreator(configuration) + return { + /** + * + * @param {Chat} [chat] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async apiChatPost(chat?: Chat, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.apiChatPost(chat, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + } +}; + +/** + * ChatApi - factory interface + * @export + */ +export const ChatApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = ChatApiFp(configuration) + return { + /** + * + * @param {Chat} [chat] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + apiChatPost(chat?: Chat, options?: any): AxiosPromise { + return localVarFp.apiChatPost(chat, options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * ChatApi - object-oriented interface + * @export + * @class ChatApi + * @extends {BaseAPI} + */ +export class ChatApi extends BaseAPI { + /** + * + * @param {Chat} [chat] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ChatApi + */ + public apiChatPost(chat?: Chat, options?: AxiosRequestConfig) { + return ChatApiFp(this.configuration).apiChatPost(chat, options).then((request) => request(this.axios, this.basePath)); + } +} + + /** * PaymentsApi - axios parameter creator * @export diff --git a/src/apis/base.ts b/src/apis/base.ts index 6962171..64532d8 100644 --- a/src/apis/base.ts +++ b/src/apis/base.ts @@ -4,7 +4,7 @@ * Genesi AI Plugin API * API set to get and manage Plugins. routes are intended to be accessed on subdomains in the format {pluginId}.Genesi.AI. * - * The version of the OpenAPI document: 1.2.6 + * The version of the OpenAPI document: 1.2.7 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/apis/common.ts b/src/apis/common.ts index cf08693..4002503 100644 --- a/src/apis/common.ts +++ b/src/apis/common.ts @@ -4,7 +4,7 @@ * Genesi AI Plugin API * API set to get and manage Plugins. routes are intended to be accessed on subdomains in the format {pluginId}.Genesi.AI. * - * The version of the OpenAPI document: 1.2.6 + * The version of the OpenAPI document: 1.2.7 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/apis/configuration.ts b/src/apis/configuration.ts index d840da4..2b23b75 100644 --- a/src/apis/configuration.ts +++ b/src/apis/configuration.ts @@ -4,7 +4,7 @@ * Genesi AI Plugin API * API set to get and manage Plugins. routes are intended to be accessed on subdomains in the format {pluginId}.Genesi.AI. * - * The version of the OpenAPI document: 1.2.6 + * The version of the OpenAPI document: 1.2.7 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/apis/index.ts b/src/apis/index.ts new file mode 100644 index 0000000..9a112cf --- /dev/null +++ b/src/apis/index.ts @@ -0,0 +1,18 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Genesi AI Plugin API + * API set to get and manage Plugins. routes are intended to be accessed on subdomains in the format {pluginId}.Genesi.AI. + * + * The version of the OpenAPI document: 1.2.7 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export * from "./api"; +export * from "./configuration"; + diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 82ebc24..8fa4c18 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -15,7 +15,7 @@ interface InputProps { } const Button = ({ - children = "change me", + children, color = "primary", type = "button", variant = "lightBg", diff --git a/src/components/ButtonLoading.tsx b/src/components/ButtonLoading.tsx index 6bea65e..d489951 100644 --- a/src/components/ButtonLoading.tsx +++ b/src/components/ButtonLoading.tsx @@ -9,6 +9,7 @@ interface InputProps { isLoading: boolean; disabled?: boolean; removeText?: boolean; + className?: ComponentProps["className"]; startIcon?: ComponentProps["startIcon"]; onClick?: ComponentProps["onClick"]; color?: ComponentProps["color"]; @@ -23,7 +24,8 @@ const ButtonLoading = ({ isLoading, disabled, onClick, - startIcon + startIcon, + className }: InputProps) => { return ( + +
+ + +
+
+ + ); +}; + +export default memo(ChatLayoutMobile); diff --git a/src/pages/PluginEditor/ChatTesting/ChatRow.tsx b/src/pages/PluginEditor/ChatTesting/ChatRow.tsx new file mode 100644 index 0000000..a3c73ee --- /dev/null +++ b/src/pages/PluginEditor/ChatTesting/ChatRow.tsx @@ -0,0 +1,54 @@ +import { Message } from "apis"; +import classNames from "classnames"; +import Typography from "components/Typography"; +import { useUserInfoCtx } from "components/UserInfo/UserInfo"; +import LogoFallback from "img/Logo.png"; +import { memo } from "react"; + +type InputProps = Message; + +const ChatRow = ({ content, role }: InputProps) => { + const { user } = useUserInfoCtx(); + return ( +
+
+ {role === "user" ? ( + + ) : ( + + )} +
+ + {content} + +
+ ); +}; + +export default memo(ChatRow); diff --git a/src/pages/PluginEditor/ChatTesting/ChatTesting.tsx b/src/pages/PluginEditor/ChatTesting/ChatTesting.tsx new file mode 100644 index 0000000..ced2b77 --- /dev/null +++ b/src/pages/PluginEditor/ChatTesting/ChatTesting.tsx @@ -0,0 +1,25 @@ +import { Theme, useMediaQuery } from "@mui/material"; +import { ReactPortal, memo, useEffect, useState } from "react"; +import { createPortal } from "react-dom"; +import { usePluginEditorCtxStatus } from "../PluginEditorCtx"; +import ChatContainer from "./ChatContainer"; +import ChatTestingMobile from "./ChatLayoutMobile"; +const ChatTesting = () => { + const { htmlRefChat } = usePluginEditorCtxStatus(); + const [portal, setPortal] = useState(null); + + useEffect(() => { + // I need a portal because I can't insert a form inside another form + setPortal(createPortal(, htmlRefChat.current!)); + }, []); + + return <>{portal}; +}; + +const ChatTestingCore = () => { + const isMd = useMediaQuery((theme) => theme.breakpoints.up("md")); + + return isMd ? : ; +}; + +export default memo(ChatTesting); diff --git a/src/pages/PluginEditor/ChatTesting/Footer.tsx b/src/pages/PluginEditor/ChatTesting/Footer.tsx new file mode 100644 index 0000000..5844ebc --- /dev/null +++ b/src/pages/PluginEditor/ChatTesting/Footer.tsx @@ -0,0 +1,60 @@ +import Button from "components/Button"; +import ButtonLoading from "components/ButtonLoading"; +import Field from "components/Field"; +import Typography from "components/Typography"; +import { memo, useEffect } from "react"; +import { useFormContext, useFormState } from "react-hook-form-mui"; + +type InputProps = { + clearChat: () => void; +}; +const Footer = ({ clearChat }: InputProps) => { + const { reset } = useFormContext(); + const { isSubmitting, isSubmitSuccessful } = useFormState(); + + useEffect(() => { + if (isSubmitSuccessful) reset(); + }, [isSubmitSuccessful, reset]); + + return ( +
+ + +
+ + + + SEND + + +
+
+ ); +}; + +export default memo(Footer); diff --git a/src/pages/PluginEditor/ChatTesting/index.tsx b/src/pages/PluginEditor/ChatTesting/index.tsx new file mode 100644 index 0000000..d71275c --- /dev/null +++ b/src/pages/PluginEditor/ChatTesting/index.tsx @@ -0,0 +1,2 @@ +import ChatTesting from "./ChatCtx"; +export default ChatTesting; diff --git a/src/pages/PluginEditor/ChatTesting/useChat.tsx b/src/pages/PluginEditor/ChatTesting/useChat.tsx new file mode 100644 index 0000000..53cea45 --- /dev/null +++ b/src/pages/PluginEditor/ChatTesting/useChat.tsx @@ -0,0 +1,101 @@ +import { ChatApi, Message } from "apis"; +import Button from "components/Button"; +import Modal from "components/Modal"; +import { useUserInfoCtx } from "components/UserInfo/UserInfo"; +import { debugConsole } from "components/util"; +import { useCallback, useEffect, useState } from "react"; +import { useFormContext } from "react-hook-form-mui"; +import { formValue } from "./ChatCtx"; + +const useChat = () => { + const { userInfo, getUserData } = useUserInfoCtx(); + const { getValues } = useFormContext(); + const [openModal, setOpenModal] = useState(false); + const [messages, setMessages] = useState([]); + + const [counter, setCounter] = useState( + userInfo.chatData?.messagesLast24H || 0 + ); + useEffect(() => { + getUserData().then( + (res) => + res?.chatData?.messagesLast24H != null && + setCounter(res?.chatData?.messagesLast24H) + ); + }, [getUserData]); + + const clearChat = useCallback(() => { + setMessages([]); + }, []); + + const sendChat = useCallback( + async ({ content }: formValue) => { + try { + let messages: Message[] = []; + setMessages((prevValue) => { + messages = [...prevValue, { content, role: "user" }]; + return messages; + }); + if (content.startsWith("§GENESI_ERROR§")) { + await new Promise((resolve) => setTimeout(resolve, 2000)); + throw new Error("Error test"); + } + if (content.startsWith("§GENESI_NO_CHATGPT§")) { + await new Promise((resolve) => setTimeout(resolve, 2000)); + setMessages((prevValue) => { + return [...prevValue, { content, role: "assistant" }]; + }); + } else { + const aiPlugin = getValues(); + const chatApi = new ChatApi(); + await chatApi.apiChatPost({ aiPlugin, messages }).then((res) => { + const { message, chatData } = res.data; + if (!message) throw new Error("No message"); + setMessages((prevValue) => { + return [...prevValue, message]; + }); + setCounter(chatData?.messagesLast24H || 0); + debugConsole(res); + }); + } + } catch (error) { + debugConsole(error); + setMessages((prevValue) => { + prevValue.splice(prevValue.length - 1, 1); + return [...prevValue]; + }); + setOpenModal(true); + } + }, + [getValues] + ); + + const resetState = useCallback(() => { + setOpenModal(false); + }, []); + + const modalError = ( + + CLOSE + + } + open={openModal} + handleClose={resetState} + titleText="Generic error" + contentText="Something went wrong, please retry." + /> + ); + + return { + counter, + sendChat, + clearChat, + messages, + modalError, + maxMessagesLast24H: userInfo.chatData?.maxMessagesLast24H || 0 + }; +}; + +export default useChat; diff --git a/src/pages/PluginEditor/PluginEditor.tsx b/src/pages/PluginEditor/PluginEditor.tsx index 2bf2742..8f86321 100644 --- a/src/pages/PluginEditor/PluginEditor.tsx +++ b/src/pages/PluginEditor/PluginEditor.tsx @@ -1,32 +1,28 @@ import { memo } from "react"; -import { BoxContainer } from "theme"; -import AlertDialog from "./AlertDialog"; +import { FormContainer } from "react-hook-form-mui"; +import ChatTesting from "./ChatTesting"; import FieldsContainer from "./Fields/FieldsContainer"; import PluginSections from "./Fields/PluginSections"; import { usePluginEditorCtxStatus } from "./PluginEditorCtx"; import PluginEditorFooter from "./PluginEditorFooter"; -import PluginEditorTitle from "./PluginEditorTitle"; const PluginEditor = () => { - const { deletePlugin, setShowDeleteDialog, error, showDeleteDialog } = - usePluginEditorCtxStatus(); + const { defaultData, savePlugin, error } = usePluginEditorCtxStatus(); return ( -
- - - - - {error &&

{error}

} - -
-
- -
+ + + + {error &&

{error}

} + +
+ +
); }; diff --git a/src/pages/PluginEditor/PluginEditorContainer.tsx b/src/pages/PluginEditor/PluginEditorContainer.tsx new file mode 100644 index 0000000..c4c055c --- /dev/null +++ b/src/pages/PluginEditor/PluginEditorContainer.tsx @@ -0,0 +1,41 @@ +import { memo } from "react"; +import { BoxContainer } from "theme"; +import AlertDialog from "./AlertDialog"; +import PluginEditor from "./PluginEditor"; +import { usePluginEditorCtxStatus } from "./PluginEditorCtx"; +import PluginEditorTitle from "./PluginEditorTitle"; + +const PluginEditorContainer = () => { + const { deletePlugin, setShowDeleteDialog, showDeleteDialog, htmlRefChat } = + usePluginEditorCtxStatus(); + return ( +
+ +
+ +
+
+
+ + + +
+
+ +
+ ); +}; + +export default memo(PluginEditorContainer); diff --git a/src/pages/PluginEditor/PluginEditorCtx.tsx b/src/pages/PluginEditor/PluginEditorCtx.tsx index ab5a4fe..9ea4f80 100644 --- a/src/pages/PluginEditor/PluginEditorCtx.tsx +++ b/src/pages/PluginEditor/PluginEditorCtx.tsx @@ -1,8 +1,7 @@ import { Button, Skeleton, Typography } from "@mui/material"; -import { createContext, useContext, useMemo } from "react"; -import { FormContainer } from "react-hook-form-mui"; +import { createContext, useContext, useMemo, useRef } from "react"; import { BoxContainer } from "theme"; -import PluginEditor from "./PluginEditor"; +import PluginEditorContainer from "./PluginEditorContainer"; import PluginEditorTitle from "./PluginEditorTitle"; import usePluginEditor from "./usePluginEditor"; @@ -14,7 +13,11 @@ type ctxTStatus = Pick< | "isNewPlugin" | "setShowDeleteDialog" | "showDeleteDialog" ->; + | "defaultData" + | "savePlugin" +> & { + htmlRefChat: React.RefObject; +}; const initialCtxStatus: ctxTStatus = { deleteInProgress: false, @@ -22,7 +25,10 @@ const initialCtxStatus: ctxTStatus = { error: "", isNewPlugin: true, setShowDeleteDialog: () => {}, - showDeleteDialog: false + showDeleteDialog: false, + defaultData: {}, + savePlugin: async () => {}, + htmlRefChat: { current: null } }; const CtxStatus = createContext(initialCtxStatus); @@ -40,6 +46,7 @@ const PluginEditorCtx = () => { showDeleteDialog, fetchData } = usePluginEditor(); + const htmlRefChat = useRef(null); const ctx = useMemo( () => ({ @@ -48,9 +55,14 @@ const PluginEditorCtx = () => { error, isNewPlugin, setShowDeleteDialog, - showDeleteDialog + showDeleteDialog, + defaultData, + savePlugin, + htmlRefChat }), [ + defaultData, + savePlugin, deleteInProgress, deletePlugin, error, @@ -90,14 +102,7 @@ const PluginEditorCtx = () => { return ( - - - + ); }; diff --git a/src/theme/BoxContainer.tsx b/src/theme/BoxContainer.tsx index 708129d..067f823 100644 --- a/src/theme/BoxContainer.tsx +++ b/src/theme/BoxContainer.tsx @@ -1,24 +1,27 @@ -import { Box, SxProps, Theme } from "@mui/material"; +import { Box } from "@mui/material"; +import classNames from "classnames"; import { memo } from "react"; type InputProps = { children: React.ReactNode; + className?: string; + removePadding?: boolean; + removeMargin?: boolean; }; -const BoxContainerSx: SxProps = { - marginY: 3, - marginX: { xs: 0, md: "16px" }, - padding: { xs: 0, md: 3 }, - borderRadius: 4 -}; - -const BoxContainer = memo(({ children }: InputProps) => ( - - {children} - -)); +const BoxContainer = memo( + ({ children, className, removePadding, removeMargin }: InputProps) => ( + + {children} + + ) +); export { BoxContainer };