Merge pull request #10031 from appwrite/chore-add-new-site-templates

Add formspree and react-admin templates to sites
This commit is contained in:
Matej Bačo 2025-12-11 16:56:03 +01:00 committed by GitHub
commit cd8e93db60
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 192 additions and 10 deletions

View file

@ -22,6 +22,8 @@ class UseCases
public const DOCUMENTATION = 'documentation';
public const BLOG = 'blog';
public const AI = 'artificial intelligence';
public const FORMS = 'forms';
public const DASHBOARD = 'dashboard';
}
const TEMPLATE_FRAMEWORKS = [
@ -1469,4 +1471,135 @@ return [
],
]
],
[
'key' => 'crm-dashboard-react-admin',
'name' => 'CRM dashboard with React Admin',
'tagline' => 'A React-based admin dashboard template with CRM features.',
'score' => 4, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [UseCases::DASHBOARD],
'screenshotDark' => $url . '/images/sites/templates/crm-dashboard-react-admin-dark.png',
'screenshotLight' => $url . '/images/sites/templates/crm-dashboard-react-admin-light.png',
'frameworks' => [
getFramework('REACT', [
'providerRootDirectory' => './react/react-admin',
'installCommand' => 'pnpm install',
'buildCommand' => 'pnpm build && pnpm db-seed',
'outputDirectory' => './dist',
]),
],
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'variables' => [
[
'name' => 'VITE_APPWRITE_ENDPOINT',
'description' => 'Endpoint of Appwrite server',
'value' => '{apiEndpoint}',
'placeholder' => '{apiEndpoint}',
'required' => true,
'type' => 'text'
],
[
'name' => 'VITE_APPWRITE_PROJECT_ID',
'description' => 'Your Appwrite project ID',
'value' => '{projectId}',
'placeholder' => '{projectId}',
'required' => true,
'type' => 'text'
],
[
'name' => 'APPWRITE_API_KEY',
'description' => 'Your Appwrite API key (for seeding only)',
'value' => '',
'placeholder' => 'a0b1...',
'required' => true,
'type' => 'password'
],
[
'name' => 'VITE_APPWRITE_DATABASE_ID',
'description' => 'Database ID (default: admin)',
'value' => 'admin',
'placeholder' => 'admin',
'required' => false,
'type' => 'text'
],
[
'name' => 'VITE_APPWRITE_TABLE_REVIEWS',
'description' => 'Table ID for reviews table',
'value' => 'reviews',
'placeholder' => 'reviews',
'required' => false,
'type' => 'text'
],
[
'name' => 'VITE_APPWRITE_TABLE_INVOICES',
'description' => 'Table ID for invoices table',
'value' => 'invoices',
'placeholder' => 'invoices',
'required' => false,
'type' => 'text'
],
[
'name' => 'VITE_APPWRITE_TABLE_ORDERS',
'description' => 'Table ID for orders table',
'value' => 'orders',
'placeholder' => 'orders',
'required' => false,
'type' => 'text'
],
[
'name' => 'VITE_APPWRITE_TABLE_PRODUCTS',
'description' => 'Table ID for products table',
'value' => 'products',
'placeholder' => 'products',
'required' => false,
'type' => 'text'
],
[
'name' => 'VITE_APPWRITE_TABLE_CATEGORIES',
'description' => 'Table ID for categories table',
'value' => 'categories',
'placeholder' => 'categories',
'required' => false,
'type' => 'text'
],
[
'name' => 'VITE_APPWRITE_TABLE_CUSTOMERS',
'description' => 'Table ID for customers table',
'value' => 'customers',
'placeholder' => 'customers',
'required' => false,
'type' => 'text'
],
]
],
[
'key' => 'job-applications-formspree',
'name' => 'Job applications form with Formspree',
'tagline' => 'A simple form submission template using Formspree.',
'score' => 4, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [UseCases::FORMS],
'screenshotDark' => $url . '/images/sites/templates/job-applications-formspree-dark.png',
'screenshotLight' => $url . '/images/sites/templates/job-applications-formspree-light.png',
'frameworks' => [
getFramework('REACT', [
'providerRootDirectory' => './react/formspree',
]),
],
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'variables' => [
[
'name' => 'VITE_FORMSPREE_FORM_ID',
'description' => 'Your Formspree form ID',
'value' => '',
'placeholder' => 'xrgkpqld',
'required' => true,
'type' => 'text'
],
]
]
];

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View file

@ -3,6 +3,7 @@
namespace Appwrite\Platform\Tasks;
// Example usage: docker compose exec appwrite screenshot --templateId="playground-for-tanstack-start"
// Example of env vars flag: --variables="{\"VITE_FORMSPREE_FORM_ID\":\"xvgkbzll\", \"VITE_FORMSPREE_FORM_SECRET\":\"some_secret\"}"
// Expected output: public/images/sites/templates/playground-for-tanstack-start-light.png (and dark.png)
use Appwrite\ID;
@ -10,6 +11,7 @@ use Tests\E2E\Client;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Platform\Action;
use Utopia\System\System;
use Utopia\Validator\Text;
class Screenshot extends Action
@ -24,11 +26,24 @@ class Screenshot extends Action
$this
->desc('Create Site template screenshot')
->param('templateId', '', new Text(128), 'Template ID.')
->param('variables', '', new Text(16384), 'JSON of env variables to use when setting up the site.')
->callback($this->action(...));
}
public function action(string $templateId): void
public function action(string $templateId, string $variables): void
{
if (empty($variables)) {
$variables = [];
} else {
$variables = \json_decode($variables, true);
if (!\is_array($variables)) {
throw new \Exception('Invalid JSON in --variables flag');
}
}
if ($variables === null) {
throw new \Exception('Invalid JSON in --variables flag');
}
$templates = Config::getParam('templates-site', []);
$allowedTemplates = \array_filter($templates, function ($item) use ($templateId) {
@ -133,6 +148,11 @@ class Screenshot extends Action
$framework = $template['frameworks'][0];
// Use best specifications to prevent out-of-memory during build
$specifications = Config::getParam('specifications', []);
$specifications = array_keys($specifications);
$specification = \end($specifications);
// Create site
$site = $client->call(Client::METHOD_POST, '/sites', [
'content-type' => 'application/json',
@ -141,6 +161,7 @@ class Screenshot extends Action
'cookie' => $cookieConsole
], [
'siteId' => ID::unique(),
'specification' => $specification,
'name' => $template["name"],
'framework' => $framework['key'],
'adapter' => $framework['adapter'],
@ -162,21 +183,48 @@ class Screenshot extends Action
$siteId = $site['body']['$id'];
// Prepare API key, incase it's needed as variable
$response = $client->call(Client::METHOD_POST, '/projects/' . $projectId . '/keys', [
'content-type' => 'application/json',
'x-appwrite-project' => 'console',
'cookie' => $cookieConsole
], [
'name' => 'Screenshot API key',
'scopes' => \array_keys(Config::getParam('scopes', []))
]);
if ($response['headers']['status-code'] !== 201) {
Console::error(\json_encode($response));
throw new \Exception("Failed to create API key");
}
$apiKey = $response['body']['secret'];
Console::info("API key created");
$variables['APPWRITE_API_KEY'] = $apiKey;
// Create variables
if (!empty($template['variables'] ?? [])) {
foreach ($template['variables'] as $variable) {
if (empty($variable['value'] ?? '')) {
if (($variable['required'] ?? false) === true) {
throw new \Exception("Missing required variable: {$variable['name']}");
}
continue;
}
$name = $variable['name'];
$value = $variable['value'];
$value = \str_replace('{projectName}', $projectName, $value);
$value = \str_replace('{projectId}', $projectId, $value);
$value = \str_replace('{apiEndpoint}', 'http://localhost/v1', $value);
$value = \str_replace('{apiEndpoint}', 'http://' . System::getEnv('_APP_DOMAIN', '') . '/v1', $value);
if (\array_key_exists($name, $variables)) {
$value = $variables[$name];
}
if (empty($value)) {
if (($variable['required'] ?? false) === true) {
throw new \Exception("Missing required variable: {$variable['name']}. Provide it using the --variables flag to resolve this.");
}
continue;
}
$response = $client->call(Client::METHOD_POST, '/sites/' . $siteId . '/variables', [
'content-type' => 'application/json',
@ -207,7 +255,8 @@ class Screenshot extends Action
'owner' => $template['providerOwner'],
'repository' => $template['providerRepositoryId'],
'rootDirectory' => $framework['providerRootDirectory'],
'version' => $template['providerVersion'],
'reference' => $template['providerVersion'],
'type' => 'tag',
'activate' => true,
]);