Template Preview
+ class="mb-4 p-4 bg-gray-50 rounded-lg"/>
Frequently asked questions
-
{{ ques.question }}
-
+
{{ ques.question }}
+
From 4bb2b59132bf46caaa23bfc978236d328ee4b6c6 Mon Sep 17 00:00:00 2001
From: Julien Nahum
Date: Thu, 23 Mar 2023 14:07:55 +0100
Subject: [PATCH 2/3] WIP
---
app/Console/Commands/GenerateTemplate.php | 25 ++++++++++++++++-------
app/Http/Controllers/SpaController.php | 2 --
app/Service/OpenAi/GptCompleter.php | 6 +++---
app/Service/SeoMetaResolver.php | 2 +-
4 files changed, 22 insertions(+), 13 deletions(-)
diff --git a/app/Console/Commands/GenerateTemplate.php b/app/Console/Commands/GenerateTemplate.php
index dfa3d02..f408553 100644
--- a/app/Console/Commands/GenerateTemplate.php
+++ b/app/Console/Commands/GenerateTemplate.php
@@ -130,6 +130,18 @@ class GenerateTemplate extends Command
}
```
+ For the type "number" you can set the property "is_rating" to "true" to turn it into a star rating input.
+
+ If the form is too long, you can paginate it by adding a page break block in the list of properties:
+ ```json
+ {
+ "name":"Page Break",
+ "next_btn_text":"Next",
+ "previous_btn_text":"Previous",
+ "type":"nf-page-break",
+ }
+ ```
+
Give me the JSON code only, for the following form: "[REPLACE]"
Do not ask me for more information about required properties or types, suggest me a form structure instead.
EOD;
@@ -163,27 +175,24 @@ class GenerateTemplate extends Command
public function handle()
{
// Get form structture
- $completer = new GptCompleter(config('services.openai.api_key'));
+ $completer = (new GptCompleter(config('services.openai.api_key')))
+ ->setSystemMessage('You are a robot helping to generate forms.');
$completer->completeChat([
- ["role" => "system", "content" => "You are a robot helping to generate forms."],
["role" => "user", "content" => Str::of(self::FORM_STRUCTURE_PROMPT)->replace('[REPLACE]', $this->argument('prompt'))->toString()]
- ],3000);
+ ], 3000);
$formData = $completer->getArray();
// Now get description and QAs
$formDescriptionPrompt = Str::of(self::FORM_DESCRIPTION_PROMPT)->replace('[REPLACE]', $this->argument('prompt'))->toString();
$formDescription = $completer->completeChat([
- ["role" => "system", "content" => "You are a robot helping to generate forms."],
["role" => "user", "content" => $formDescriptionPrompt]
])->getString();
$formQAs = $completer->completeChat([
- ["role" => "system", "content" => "You are a robot helping to generate forms."],
["role" => "user", "content" => $formDescriptionPrompt],
["role" => "assistant", "content" => $formDescription],
["role" => "user", "content" => self::FORM_QAS_PROMPT]
])->getArray();
$formTitle = $completer->completeChat([
- ["role" => "system", "content" => "You are a robot helping to generate forms."],
["role" => "user", "content" => $formDescriptionPrompt],
["role" => "assistant", "content" => $formDescription],
["role" => "user", "content" => self::FORM_TITLE_PROMPT]
@@ -191,7 +200,6 @@ class GenerateTemplate extends Command
// Finally get keyworks for image cover
$formCoverKeyworks = $completer->completeChat([
- ["role" => "system", "content" => "You are a robot helping to generate forms."],
["role" => "user", "content" => $formDescriptionPrompt],
["role" => "assistant", "content" => $formDescription],
["role" => "user", "content" => self::FORM_IMG_KEYWORDS_PROMPT]
@@ -225,6 +233,9 @@ class GenerateTemplate extends Command
$property['id'] = Str::uuid()->toString();
}
+ // Clean data
+ $formTitle = Str::of($formTitle)->replace('"', '')->toString();
+
return Template::create([
'name' => $formTitle,
'description' => $formDescription,
diff --git a/app/Http/Controllers/SpaController.php b/app/Http/Controllers/SpaController.php
index eb4ce03..50456f1 100644
--- a/app/Http/Controllers/SpaController.php
+++ b/app/Http/Controllers/SpaController.php
@@ -8,8 +8,6 @@ class SpaController extends Controller
{
/**
* Get the SPA view.
- *
- * @return \Illuminate\Http\Response
*/
public function __invoke(Request $request)
{
diff --git a/app/Service/OpenAi/GptCompleter.php b/app/Service/OpenAi/GptCompleter.php
index c5e5b5a..797c8c9 100644
--- a/app/Service/OpenAi/GptCompleter.php
+++ b/app/Service/OpenAi/GptCompleter.php
@@ -92,11 +92,11 @@ class GptCompleter
protected function computeChatCompletion(array $messages, int $maxTokens = 512, float $temperature = 0.81): self
{
- if (isset($this->systemMessage)) {
- $messages = array_merge([
+ if (isset($this->systemMessage) && $messages[0]['role'] !== 'system') {
+ $messages = array_merge([[
'role' => 'system',
'content' => $this->systemMessage
- ], $messages);
+ ]], $messages);
}
$completionInput = [
diff --git a/app/Service/SeoMetaResolver.php b/app/Service/SeoMetaResolver.php
index 7b415ae..5232562 100644
--- a/app/Service/SeoMetaResolver.php
+++ b/app/Service/SeoMetaResolver.php
@@ -178,7 +178,7 @@ class SeoMetaResolver
return [
'title' => $template->name . $this->titleSuffix(),
- 'description' => Str::of($template->description)->limit(160) ,
+ 'description' => Str::of($template->description)->limit(160),
'image' => $template->image_url
];
}
From f4ab98a2b00f889c38318d7fe9908b32d73061a8 Mon Sep 17 00:00:00 2001
From: Julien Nahum
Date: Sun, 26 Mar 2023 12:54:12 +0200
Subject: [PATCH 3/3] WIP
---
app/Console/Commands/GenerateTemplate.php | 3 +-
.../Controllers/Forms/AiFormController.php | 38 ++++++
app/Http/Requests/AiGenerateFormRequest.php | 20 +++
.../forms/create/CreateFormBaseModal.vue | 114 ++++++++++++++++++
resources/js/pages/forms/create.vue | 14 ++-
resources/js/pages/welcome.vue | 5 +-
resources/views/spa.blade.php | 1 +
routes/api.php | 5 +-
8 files changed, 194 insertions(+), 6 deletions(-)
create mode 100644 app/Http/Controllers/Forms/AiFormController.php
create mode 100644 app/Http/Requests/AiGenerateFormRequest.php
create mode 100644 resources/js/components/pages/forms/create/CreateFormBaseModal.vue
diff --git a/app/Console/Commands/GenerateTemplate.php b/app/Console/Commands/GenerateTemplate.php
index f408553..c16e211 100644
--- a/app/Console/Commands/GenerateTemplate.php
+++ b/app/Console/Commands/GenerateTemplate.php
@@ -28,7 +28,7 @@ class GenerateTemplate extends Command
I created a form builder. Forms are represented as Json objects. Here's an example form:
```json
{
- "title": "Contact Form",
+ "title": "Contact Us",
"properties": [
{
"help": null,
@@ -219,7 +219,6 @@ class GenerateTemplate extends Command
{
$url = 'https://api.unsplash.com/search/photos?query=' . urlencode($searchQuery) . '&client_id=' . config('services.unslash.access_key');
$response = Http::get($url)->json();
- ray($response, $url);
if (isset($response['results'][0]['urls']['regular'])) {
return $response['results'][0]['urls']['regular'];
}
diff --git a/app/Http/Controllers/Forms/AiFormController.php b/app/Http/Controllers/Forms/AiFormController.php
new file mode 100644
index 0000000..aa371fb
--- /dev/null
+++ b/app/Http/Controllers/Forms/AiFormController.php
@@ -0,0 +1,38 @@
+middleware('throttle:4,1');
+ $completer = (new GptCompleter(config('services.openai.api_key')))
+ ->setSystemMessage('You are a robot helping to generate forms.');
+ $completer->completeChat([
+ ["role" => "user", "content" => Str::of(GenerateTemplate::FORM_STRUCTURE_PROMPT)
+ ->replace('[REPLACE]', $request->form_prompt)->toString()]
+ ], 3000);
+
+ return $this->success([
+ 'message' => 'Form successfully generated!',
+ 'form' => $this->cleanOutput($completer->getArray())
+ ]);
+ }
+
+ private function cleanOutput($formData)
+ {
+ // Add property uuids
+ foreach ($formData['properties'] as &$property) {
+ $property['id'] = Str::uuid()->toString();
+ }
+
+ return $formData;
+ }
+}
diff --git a/app/Http/Requests/AiGenerateFormRequest.php b/app/Http/Requests/AiGenerateFormRequest.php
new file mode 100644
index 0000000..6aac735
--- /dev/null
+++ b/app/Http/Requests/AiGenerateFormRequest.php
@@ -0,0 +1,20 @@
+
+ */
+ public function rules()
+ {
+ return [
+ 'form_prompt' => 'required|string'
+ ];
+ }
+}
diff --git a/resources/js/components/pages/forms/create/CreateFormBaseModal.vue b/resources/js/components/pages/forms/create/CreateFormBaseModal.vue
new file mode 100644
index 0000000..3132028
--- /dev/null
+++ b/resources/js/components/pages/forms/create/CreateFormBaseModal.vue
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Choose a base for your form
+
+
+ AI-powered form generator
+
+
+
+
+
+
Start from a simple contact form
+
+
+
+
Use our AI to create the form
+
(1 min)
+
+
+
+
Start from a template
+
+
+
+
+
+
+
+
diff --git a/resources/js/pages/forms/create.vue b/resources/js/pages/forms/create.vue
index 76c626c..ef21c5a 100644
--- a/resources/js/pages/forms/create.vue
+++ b/resources/js/pages/forms/create.vue
@@ -2,6 +2,8 @@
+
it's free.
-
+
+ Create a form for FREE
+
+
Create a form for FREE
diff --git a/resources/views/spa.blade.php b/resources/views/spa.blade.php
index 7a369f2..f9dc12d 100644
--- a/resources/views/spa.blade.php
+++ b/resources/views/spa.blade.php
@@ -13,6 +13,7 @@
'google_analytics_code' => config('services.google_analytics_code'),
'amplitude_code' => config('services.amplitude_code'),
'crisp_website_id' => config('services.crisp_website_id'),
+ 'ai_features_enabled' => !is_null(config('services.openai.api_key'))
];
@endphp
diff --git a/routes/api.php b/routes/api.php
index da24172..90968fe 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -144,6 +144,9 @@ Route::prefix('forms')->name('forms.')->group(function () {
// File uploads
Route::get('assets/{assetFileName}', [PublicFormController::class, 'showAsset'])->name('assets.show');
+
+ // AI
+ Route::post('ai/generate', [\App\Http\Controllers\Forms\AiFormController::class, 'generateForm'])->name('ai.generate');
});
/**
@@ -155,4 +158,4 @@ Route::prefix('content')->name('content.')->group(function () {
// Templates
Route::get('templates', [TemplateController::class, 'index'])->name('templates.show');
-Route::post('templates', [TemplateController::class, 'create'])->name('templates.create');
\ No newline at end of file
+Route::post('templates', [TemplateController::class, 'create'])->name('templates.create');