create new bot flow
This commit is contained in:
parent
99987f8444
commit
90dfc3dec4
4 changed files with 140 additions and 4 deletions
|
@ -134,17 +134,25 @@ pub struct BotParams {
|
||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: can we unify this with save_bot?
|
||||||
pub async fn create_bot(
|
pub async fn create_bot(
|
||||||
conn: DatabaseConnection,
|
conn: DatabaseConnection,
|
||||||
user: User,
|
user: User,
|
||||||
params: Json<BotParams>,
|
params: Json<BotParams>,
|
||||||
) -> (StatusCode, Json<Bot>) {
|
) -> Result<(StatusCode, Json<Bot>), SaveBotError> {
|
||||||
|
validate_bot_name(¶ms.name)?;
|
||||||
|
let existing_bot = bots::find_bot_by_name(¶ms.name, &conn)
|
||||||
|
.optional()
|
||||||
|
.expect("could not run query");
|
||||||
|
if existing_bot.is_some() {
|
||||||
|
return Err(SaveBotError::BotNameTaken);
|
||||||
|
}
|
||||||
let bot_params = bots::NewBot {
|
let bot_params = bots::NewBot {
|
||||||
owner_id: Some(user.id),
|
owner_id: Some(user.id),
|
||||||
name: ¶ms.name,
|
name: ¶ms.name,
|
||||||
};
|
};
|
||||||
let bot = bots::create_bot(&bot_params, &conn).unwrap();
|
let bot = bots::create_bot(&bot_params, &conn).unwrap();
|
||||||
(StatusCode::CREATED, Json(bot))
|
Ok((StatusCode::CREATED, Json(bot)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: handle errors
|
// TODO: handle errors
|
||||||
|
|
98
web/pw-server/src/routes/bots/new.svelte
Normal file
98
web/pw-server/src/routes/bots/new.svelte
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { goto } from "$app/navigation";
|
||||||
|
import { get_session_token } from "$lib/auth";
|
||||||
|
import { currentUser } from "$lib/stores/current_user";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
let botName: string | undefined = undefined;
|
||||||
|
let saveErrors: string[] = [];
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
// ensure user is logged in
|
||||||
|
if (!$currentUser) {
|
||||||
|
goto("/login");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function createBot() {
|
||||||
|
saveErrors = [];
|
||||||
|
|
||||||
|
let response = await fetch("/api/bots", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bearer ${get_session_token()}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
name: botName,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
let responseData = await response.json();
|
||||||
|
if (response.ok) {
|
||||||
|
let bot = responseData;
|
||||||
|
goto(`/bots/${bot["name"]}`);
|
||||||
|
} else {
|
||||||
|
const error = responseData["error"];
|
||||||
|
if (error["type"] === "validation_failed") {
|
||||||
|
saveErrors = error["validation_errors"];
|
||||||
|
} else if (error["type"] === "bot_name_taken") {
|
||||||
|
saveErrors = ["Bot name is already taken"];
|
||||||
|
} else {
|
||||||
|
// unexpected error
|
||||||
|
throw responseData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="create-bot-form">
|
||||||
|
<h4>Create new bot</h4>
|
||||||
|
<input type="text" class="bot-name-input" placeholder="bot name" bind:value={botName} />
|
||||||
|
{#if saveErrors.length > 0}
|
||||||
|
<ul>
|
||||||
|
{#each saveErrors as errorText}
|
||||||
|
<li class="error-text">{errorText}</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
|
<button class="submit-button save-button" on:click={createBot}>Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.container {
|
||||||
|
width: 400px;
|
||||||
|
max-width: 80%;
|
||||||
|
margin: 50px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-bot-form h4 {
|
||||||
|
margin-bottom: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-text {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-button {
|
||||||
|
padding: 6px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 0;
|
||||||
|
font-size: 18pt;
|
||||||
|
display: block;
|
||||||
|
margin: 10px auto;
|
||||||
|
background-color: lightgreen;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bot-name-input {
|
||||||
|
width: 100%;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 10px 0;
|
||||||
|
border: 1px solid rgb(216, 219, 223);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,6 +1,5 @@
|
||||||
<script lang="ts" context="module">
|
<script lang="ts" context="module">
|
||||||
import { get_session_token } from "$lib/auth";
|
import { get_session_token } from "$lib/auth";
|
||||||
import { mount_component } from "svelte/internal";
|
|
||||||
|
|
||||||
export async function load({ page }) {
|
export async function load({ page }) {
|
||||||
const token = get_session_token();
|
const token = get_session_token();
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { currentUser } from "$lib/stores/current_user";
|
||||||
|
|
||||||
export let userName: string;
|
export let userName: string;
|
||||||
export let bots: object[];
|
export let bots: object[];
|
||||||
</script>
|
</script>
|
||||||
|
@ -25,7 +27,13 @@
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h1 class="user-name">{userName}</h1>
|
<h1 class="user-name">{userName}</h1>
|
||||||
</div>
|
</div>
|
||||||
<h2>Bots</h2>
|
|
||||||
|
<div class="bot-list-header">
|
||||||
|
<h2 class="bot-list-header-title">Bots</h2>
|
||||||
|
{#if $currentUser && $currentUser.username == userName}
|
||||||
|
<a href="/bots/new" class="btn-new-bot"> New bot </a>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
<ul class="bot-list">
|
<ul class="bot-list">
|
||||||
{#each bots as bot}
|
{#each bots as bot}
|
||||||
<li class="bot">
|
<li class="bot">
|
||||||
|
@ -51,6 +59,29 @@
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bot-list-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bot-list-header-title {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-new-bot {
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 0;
|
||||||
|
display: block;
|
||||||
|
color: white;
|
||||||
|
background-color: rgb(40, 167, 69);
|
||||||
|
font-weight: 500;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 11pt;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.bot-list {
|
.bot-list {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
Loading…
Reference in a new issue