-
Your Spaces
-
+
Your Spaces
+
{#if spaces.length === 0}
No spaces yet. Create your first space to get started!
@@ -257,19 +330,16 @@
{#each spaces as space}
-
+
Space ID: {space.id}
{#if space.url}
-
URL:
+
URL:
{space.url}
@@ -277,11 +347,11 @@
{/if}
Created: {new Date(space.created_at).toLocaleString()}
-
+
{#if actionError[space.id]}
-
{actionError[space.id]}
+
{actionError[space.id]}
{/if}
-
+
{#if actionLoading[space.id]}
{:else}
{#if space.status?.toLowerCase() === 'running'}
-
{#if space.url}
-
@@ -306,15 +376,15 @@
{/if}
{:else}
-
{/if}
-
-
-
\ No newline at end of file
diff --git a/client/src/styles/auth.css b/client/src/styles/auth.css
new file mode 100644
index 0000000..7b9f874
--- /dev/null
+++ b/client/src/styles/auth.css
@@ -0,0 +1,322 @@
+.auth-container {
+ display: flex;
+ min-height: 100vh;
+ background: #f9fafc;
+ position: relative;
+ overflow: hidden;
+}
+
+.auth-panel {
+ width: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: absolute;
+ height: 100%;
+ top: 0;
+ transition: left 1s cubic-bezier(0.65, 0, 0.35, 1), right 1s cubic-bezier(0.65, 0, 0.35, 1);
+}
+
+.auth-form-panel {
+ background: #ffffff;
+ left: 0;
+ padding: 64px;
+}
+
+.auth-image-panel {
+ background: #f9fafc;
+ right: 0;
+ padding: 0;
+}
+
+.auth-container.signup-mode .auth-form-panel {
+ left: 50%;
+}
+
+.auth-container.signup-mode .auth-image-panel {
+ right: 50%;
+}
+
+.auth-form-content {
+ width: 100%;
+ max-width: 440px;
+}
+
+.auth-image-content {
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+
+.auth-image {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+.auth-image-content::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 120px;
+ background: linear-gradient(to bottom, rgba(249, 250, 252, 0.3) 0%, transparent 100%);
+ pointer-events: none;
+ z-index: 1;
+}
+
+.auth-image-content::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ width: 60px;
+ background: linear-gradient(to right, rgba(249, 250, 252, 0.2) 0%, transparent 100%);
+ pointer-events: none;
+ z-index: 1;
+}
+
+.image-caption {
+ position: absolute;
+ bottom: 32px;
+ left: 32px;
+ font-size: 16px;
+ font-weight: bold;
+ color: #ffffff;
+ z-index: 2;
+ text-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
+}
+
+.image-caption a {
+ color: #ffffff;
+ text-decoration: underline;
+ transition: opacity 0.125s ease-in-out;
+}
+
+.image-caption a:hover {
+ opacity: 0.8;
+}
+
+.flag-banner {
+ position: absolute;
+ top: 0;
+ left: 10px;
+ width: 256px;
+ z-index: 999;
+ border: 0;
+}
+
+.auth-header {
+ margin-bottom: 48px;
+}
+
+.auth-logo {
+ width: 56px;
+ height: 56px;
+ margin-bottom: 16px;
+}
+
+.auth-title {
+ font-size: 36px;
+ font-weight: bold;
+ color: #1f2d3d;
+ margin: 0 0 8px 0;
+ line-height: 1.2;
+ transition: opacity 0.3s ease-in-out;
+}
+
+.auth-subtitle {
+ font-size: 18px;
+ color: #8492a6;
+ margin: 0;
+ line-height: 1.5;
+ transition: opacity 0.3s ease-in-out;
+}
+
+.auth-mode-switch {
+ text-align: center;
+ margin-top: 24px;
+ font-size: 16px;
+ color: #8492a6;
+}
+
+.auth-mode-link {
+ color: #ec3750;
+ font-weight: bold;
+ cursor: pointer;
+ transition: color 0.125s ease-in-out;
+ text-decoration: underline;
+}
+
+.auth-mode-link:hover {
+ color: #ff8c37;
+}
+
+.form-group {
+ margin-bottom: 24px;
+ transition: opacity 0.6s ease-in-out, max-height 0.8s ease-in-out, margin 0.8s ease-in-out;
+}
+
+.username-field {
+ max-height: 0;
+ opacity: 0;
+ margin-bottom: 0;
+ overflow: hidden;
+}
+
+.username-field.show {
+ max-height: 200px;
+ opacity: 1;
+ margin-bottom: 24px;
+}
+
+.form-label {
+ display: block;
+ margin-bottom: 8px;
+ font-size: 16px;
+ font-weight: bold;
+ color: #1f2d3d;
+}
+
+.form-input {
+ width: 100%;
+ padding: 14px 16px;
+ border: 2px solid #e0e6ed;
+ border-radius: 8px;
+ background: #ffffff;
+ color: #1f2d3d;
+ font-size: 16px;
+ font-family: Phantom Sans, sans-serif;
+ box-sizing: border-box;
+ transition: border-color 0.125s ease-in-out, box-shadow 0.125s ease-in-out;
+}
+
+.form-input:focus {
+ outline: none;
+ border-color: #338eda;
+ box-shadow: 0 0 0 3px rgba(51, 142, 218, 0.1);
+}
+
+.form-input::placeholder {
+ color: #8492a6;
+}
+
+.primary-button {
+ width: 100%;
+ padding: 16px 32px;
+ margin-top: 8px;
+ border: none;
+ border-radius: 99999px;
+ background: #ec3750;
+ color: #ffffff;
+ font-size: 18px;
+ font-weight: bold;
+ font-family: Phantom Sans, sans-serif;
+ cursor: pointer;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.125);
+ transition: transform 0.125s ease-in-out, box-shadow 0.125s ease-in-out;
+ -webkit-tap-highlight-color: transparent;
+}
+
+.primary-button:hover:not(:disabled) {
+ transform: scale(1.0625);
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
+}
+
+.primary-button:active:not(:disabled) {
+ transform: scale(1);
+}
+
+.primary-button:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+.secondary-button {
+ width: 100%;
+ padding: 16px 32px;
+ margin-top: 16px;
+ background: transparent;
+ border: 2px solid #ec3750;
+ border-radius: 99999px;
+ color: #ec3750;
+ font-size: 18px;
+ font-weight: bold;
+ font-family: Phantom Sans, sans-serif;
+ cursor: pointer;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ transition: transform 0.125s ease-in-out, box-shadow 0.125s ease-in-out;
+ -webkit-tap-highlight-color: transparent;
+}
+
+.secondary-button:hover {
+ transform: scale(1.0625);
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.125);
+}
+
+.secondary-button:active {
+ transform: scale(1);
+}
+
+.error-message {
+ padding: 12px 16px;
+ margin-bottom: 16px;
+ background: rgba(236, 55, 80, 0.1);
+ border: 2px solid #ec3750;
+ border-radius: 8px;
+ color: #ec3750;
+ font-size: 16px;
+}
+
+.success-message {
+ padding: 12px 16px;
+ margin-bottom: 16px;
+ background: rgba(51, 214, 166, 0.1);
+ border: 2px solid #33d6a6;
+ border-radius: 8px;
+ color: #33d6a6;
+ font-size: 16px;
+}
+
+.info-message {
+ text-align: center;
+ margin-bottom: 24px;
+ color: #8492a6;
+ font-size: 16px;
+}
+
+
+@media (max-width: 768px) {
+ .auth-container {
+ flex-direction: column;
+ }
+
+ .auth-panel {
+ padding: 32px;
+ }
+
+ .auth-form-panel {
+ order: 1 !important;
+ min-height: auto;
+ }
+
+ .auth-image-panel {
+ order: 2 !important;
+ min-height: 300px;
+ }
+
+ .flag-banner {
+ width: 128px;
+ }
+
+ .auth-title {
+ font-size: 28px;
+ }
+}
diff --git a/client/src/styles/dashboard.css b/client/src/styles/dashboard.css
new file mode 100644
index 0000000..c517530
--- /dev/null
+++ b/client/src/styles/dashboard.css
@@ -0,0 +1,503 @@
+.dashboard {
+ min-height: 100vh;
+ padding: 32px;
+ background: #f9fafc;
+}
+
+.dashboard-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 64px;
+ padding-bottom: 32px;
+ border-bottom: 2px solid #e0e6ed;
+}
+
+.header-content {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+}
+
+.dashboard-logo {
+ width: 48px;
+ height: 48px;
+}
+
+.dashboard-title {
+ font-size: 32px;
+ font-weight: bold;
+ color: #1f2d3d;
+ margin: 0;
+}
+
+.welcome-text {
+ margin: 8px 0 0 0;
+ font-size: 16px;
+ color: #8492a6;
+}
+
+.signout-button {
+ padding: 12px 24px;
+ background: transparent;
+ border: 2px solid #ec3750;
+ border-radius: 99999px;
+ color: #ec3750;
+ font-size: 16px;
+ font-weight: bold;
+ font-family: Phantom Sans, sans-serif;
+ cursor: pointer;
+ transition: transform 0.125s ease-in-out, box-shadow 0.125s ease-in-out;
+ -webkit-tap-highlight-color: transparent;
+}
+
+.signout-button:hover {
+ transform: scale(1.0625);
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.125);
+}
+
+.signout-button:active {
+ transform: scale(1);
+}
+
+.dashboard-content {
+ max-width: 1200px;
+ margin: 0 auto;
+}
+
+.actions-bar {
+ display: flex;
+ gap: 16px;
+ margin-bottom: 32px;
+}
+
+.btn-primary {
+ padding: 14px 32px;
+ background: #ec3750;
+ color: #ffffff;
+ border: none;
+ border-radius: 99999px;
+ font-size: 16px;
+ font-weight: bold;
+ font-family: Phantom Sans, sans-serif;
+ cursor: pointer;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.125);
+ transition: transform 0.125s ease-in-out, box-shadow 0.125s ease-in-out;
+ -webkit-tap-highlight-color: transparent;
+}
+
+.btn-primary:hover:not(:disabled) {
+ transform: scale(1.0625);
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
+}
+
+.btn-primary:active:not(:disabled) {
+ transform: scale(1);
+}
+
+.btn-primary:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+.btn-secondary {
+ padding: 14px 32px;
+ background: transparent;
+ border: 2px solid #ec3750;
+ border-radius: 99999px;
+ color: #ec3750;
+ font-size: 16px;
+ font-weight: bold;
+ font-family: Phantom Sans, sans-serif;
+ cursor: pointer;
+ transition: transform 0.125s ease-in-out, box-shadow 0.125s ease-in-out;
+ -webkit-tap-highlight-color: transparent;
+}
+
+.btn-secondary:hover {
+ transform: scale(1.0625);
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.125);
+}
+
+.btn-secondary:active {
+ transform: scale(1);
+}
+
+.create-form {
+ background: #ffffff;
+ padding: 32px;
+ border-radius: 8px;
+ margin-bottom: 32px;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.125);
+}
+
+.create-form-title {
+ margin: 0 0 24px 0;
+ font-size: 24px;
+ font-weight: bold;
+ color: #1f2d3d;
+}
+
+.form-group {
+ margin-bottom: 24px;
+}
+
+.form-label {
+ display: block;
+ margin-bottom: 8px;
+ font-size: 16px;
+ font-weight: bold;
+ color: #1f2d3d;
+}
+
+.form-input {
+ width: 100%;
+ padding: 14px 16px;
+ border: 2px solid #e0e6ed;
+ border-radius: 8px;
+ background: #ffffff;
+ color: #1f2d3d;
+ font-size: 16px;
+ font-family: Phantom Sans, sans-serif;
+ box-sizing: border-box;
+ transition: border-color 0.125s ease-in-out, box-shadow 0.125s ease-in-out;
+}
+
+.form-input:focus {
+ outline: none;
+ border-color: #338eda;
+ box-shadow: 0 0 0 3px rgba(51, 142, 218, 0.1);
+}
+
+.password-input-wrapper {
+ position: relative;
+ width: 100%;
+}
+
+.password-input {
+ padding-right: 48px;
+}
+
+.password-toggle {
+ position: absolute;
+ right: 12px;
+ top: 50%;
+ transform: translateY(-50%);
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ padding: 4px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: opacity 0.125s ease-in-out;
+}
+
+.password-toggle:hover {
+ opacity: 0.7;
+}
+
+.password-toggle img {
+ width: 20px;
+ height: 20px;
+ filter: opacity(0.6);
+ transition: filter 0.125s ease-in-out;
+}
+
+.password-toggle:hover img {
+ filter: opacity(1);
+}
+
+.custom-select {
+ position: relative;
+ width: 100%;
+}
+
+.select-trigger {
+ width: 100%;
+ padding: 14px 16px;
+ padding-right: 48px;
+ border: 2px solid #e0e6ed;
+ border-radius: 8px;
+ background: #ffffff;
+ color: #1f2d3d;
+ font-size: 16px;
+ font-family: Phantom Sans, sans-serif;
+ font-weight: bold;
+ cursor: pointer;
+ box-sizing: border-box;
+ transition: border-color 0.125s ease-in-out, box-shadow 0.125s ease-in-out;
+ text-align: left;
+}
+
+.select-trigger:hover {
+ border-color: #338eda;
+}
+
+.select-trigger.open {
+ border-color: #338eda;
+ box-shadow: 0 0 0 3px rgba(51, 142, 218, 0.1);
+}
+
+.select-arrow {
+ position: absolute;
+ right: 16px;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 0;
+ height: 0;
+ border-left: 6px solid transparent;
+ border-right: 6px solid transparent;
+ border-top: 6px solid #3c4858;
+ transition: transform 0.125s ease-in-out;
+ pointer-events: none;
+}
+
+.select-arrow.open {
+ transform: translateY(-50%) rotate(180deg);
+}
+
+.select-dropdown {
+ position: absolute;
+ top: calc(100% + 8px);
+ left: 0;
+ right: 0;
+ background: #ffffff;
+ border: 2px solid #338eda;
+ border-radius: 8px;
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
+ z-index: 100;
+ max-height: 0;
+ overflow: hidden;
+ opacity: 0;
+ transition: max-height 0.25s ease-in-out, opacity 0.25s ease-in-out;
+}
+
+.select-dropdown.open {
+ max-height: 300px;
+ opacity: 1;
+ overflow-y: auto;
+}
+
+.select-option {
+ padding: 14px 16px;
+ cursor: pointer;
+ transition: background-color 0.125s ease-in-out;
+ border-bottom: 1px solid #e0e6ed;
+}
+
+.select-option:last-child {
+ border-bottom: none;
+}
+
+.select-option:hover {
+ background: #f9fafc;
+}
+
+.select-option.selected {
+ background: rgba(51, 142, 218, 0.1);
+ color: #338eda;
+ font-weight: bold;
+}
+
+.option-label {
+ font-size: 16px;
+ font-weight: bold;
+ color: #1f2d3d;
+ margin-bottom: 4px;
+}
+
+.option-description {
+ font-size: 14px;
+ color: #8492a6;
+}
+
+.error-message {
+ padding: 12px 16px;
+ margin-bottom: 16px;
+ background: rgba(236, 55, 80, 0.1);
+ border: 2px solid #ec3750;
+ border-radius: 8px;
+ color: #ec3750;
+ font-size: 16px;
+}
+
+.error-message.small {
+ padding: 8px 12px;
+ font-size: 14px;
+}
+
+.section-title {
+ font-size: 24px;
+ font-weight: bold;
+ color: #1f2d3d;
+ margin: 0 0 24px 0;
+}
+
+.empty-state {
+ text-align: center;
+ padding: 64px 32px;
+ color: #8492a6;
+ font-size: 16px;
+}
+
+.spaces-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
+ gap: 24px;
+}
+
+.space-card {
+ background: #ffffff;
+ padding: 24px;
+ border-radius: 8px;
+ border: 2px solid #e0e6ed;
+ transition: border-color 0.125s ease-in-out, box-shadow 0.125s ease-in-out;
+}
+
+.space-card:hover {
+ border-color: #338eda;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.125);
+}
+
+.space-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 16px;
+}
+
+.space-type {
+ font-size: 20px;
+ font-weight: bold;
+ color: #1f2d3d;
+ margin: 0;
+ text-transform: capitalize;
+}
+
+.status-badge {
+ padding: 4px 12px;
+ border-radius: 99999px;
+ font-size: 14px;
+ font-weight: bold;
+ color: #ffffff;
+}
+
+.status-running {
+ background: #33d6a6;
+}
+
+.status-stopped {
+ background: #ec3750;
+}
+
+.status-created {
+ background: #f1c40f;
+}
+
+.status-unknown {
+ background: #8492a6;
+}
+
+.space-info {
+ margin-bottom: 16px;
+ font-size: 14px;
+}
+
+.space-info p {
+ margin: 8px 0;
+ color: #8492a6;
+}
+
+.space-info strong {
+ color: #3c4858;
+}
+
+.space-info a {
+ color: #338eda;
+ text-decoration: none;
+ transition: color 0.125s ease-in-out;
+}
+
+.space-info a:hover {
+ color: #ec3750;
+ text-decoration: underline;
+}
+
+.space-actions {
+ display: flex;
+ gap: 8px;
+}
+
+.action-btn {
+ flex: 1;
+ padding: 10px 16px;
+ border-radius: 99999px;
+ border: none;
+ font-size: 14px;
+ font-weight: bold;
+ font-family: Phantom Sans, sans-serif;
+ cursor: pointer;
+ transition: transform 0.125s ease-in-out;
+ text-decoration: none;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ -webkit-tap-highlight-color: transparent;
+}
+
+.action-btn:hover:not(:disabled) {
+ transform: scale(1.0625);
+}
+
+.action-btn:active:not(:disabled) {
+ transform: scale(1);
+}
+
+.action-btn:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+.action-btn.start {
+ background: #33d6a6;
+ color: #ffffff;
+}
+
+.action-btn.stop {
+ background: #ec3750;
+ color: #ffffff;
+}
+
+.action-btn.open {
+ background: #338eda;
+ color: #ffffff;
+}
+
+.action-btn.refresh {
+ background: transparent;
+ border: 2px solid #338eda;
+ color: #338eda;
+ flex: 0 0 auto;
+ min-width: 44px;
+}
+
+@media (max-width: 768px) {
+ .dashboard {
+ padding: 16px;
+ }
+
+ .dashboard-header {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 16px;
+ }
+
+ .actions-bar {
+ flex-direction: column;
+ }
+
+ .spaces-grid {
+ grid-template-columns: 1fr;
+ }
+}