mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-03-08 01:53:30 +01:00
feat: Phase 5 BYOB foundation + Phase 3 GitHub integration
Phase 5 - BYOB Backend (TASK-007A/B): - LocalSQL Adapter with full CloudStore API compatibility - QueryBuilder translates Parse-style queries to SQL - SchemaManager with PostgreSQL/Supabase export - LocalBackendServer with REST endpoints - BackendManager with IPC handlers for Electron - In-memory fallback when better-sqlite3 unavailable Phase 3 - GitHub Panel (GIT-004): - Issues tab with list/detail views - Pull Requests tab with list/detail views - GitHub API client with OAuth support - Repository info hook integration Phase 3 - Editor UX Bugfixes (TASK-013): - Legacy runtime detection banners - Read-only enforcement for legacy projects - Code editor modal close improvements - Property panel stuck state fix - Blockly node deletion and UI polish Phase 11 - Cloud Functions Planning: - Architecture documentation for workflow automation - Execution history storage schema design - Canvas overlay concept for debugging Docs: Updated LEARNINGS.md and COMMON-ISSUES.md
This commit is contained in:
@@ -109,6 +109,7 @@
|
||||
|
||||
.InputWrapper {
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden; // Prevent tiny vertical scrollbar on single-line inputs
|
||||
flex-grow: 1;
|
||||
padding-top: 1px;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@ export interface LauncherProps {
|
||||
onLaunchProject?: (projectId: string) => void;
|
||||
onOpenProjectFolder?: (projectId: string) => void;
|
||||
onDeleteProject?: (projectId: string) => void;
|
||||
onMigrateProject?: (projectId: string) => void;
|
||||
onOpenReadOnly?: (projectId: string) => void;
|
||||
|
||||
// Project organization service (optional - for Storybook compatibility)
|
||||
projectOrganizationService?: any;
|
||||
@@ -178,6 +180,8 @@ export function Launcher({
|
||||
onLaunchProject,
|
||||
onOpenProjectFolder,
|
||||
onDeleteProject,
|
||||
onMigrateProject,
|
||||
onOpenReadOnly,
|
||||
projectOrganizationService,
|
||||
githubUser,
|
||||
githubIsAuthenticated,
|
||||
@@ -285,6 +289,8 @@ export function Launcher({
|
||||
onLaunchProject,
|
||||
onOpenProjectFolder,
|
||||
onDeleteProject,
|
||||
onMigrateProject,
|
||||
onOpenReadOnly,
|
||||
githubUser,
|
||||
githubIsAuthenticated,
|
||||
githubIsConnecting,
|
||||
|
||||
@@ -43,6 +43,8 @@ export interface LauncherContextValue {
|
||||
onLaunchProject?: (projectId: string) => void;
|
||||
onOpenProjectFolder?: (projectId: string) => void;
|
||||
onDeleteProject?: (projectId: string) => void;
|
||||
onMigrateProject?: (projectId: string) => void;
|
||||
onOpenReadOnly?: (projectId: string) => void;
|
||||
|
||||
// GitHub OAuth integration (optional - for Storybook compatibility)
|
||||
githubUser?: GitHubUser | null;
|
||||
|
||||
@@ -17,3 +17,32 @@
|
||||
.VersionControlTooltip {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
// Legacy project styles
|
||||
.LegacyCard {
|
||||
border-color: var(--theme-color-border-danger) !important;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--theme-color-border-danger) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.LegacyBanner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--spacing-2);
|
||||
margin-top: var(--spacing-3);
|
||||
padding: var(--spacing-2) var(--spacing-3);
|
||||
background-color: var(--theme-color-bg-danger-subtle);
|
||||
border: 1px solid var(--theme-color-border-danger);
|
||||
border-radius: var(--border-radius-medium);
|
||||
}
|
||||
|
||||
.LegacyDetails {
|
||||
margin-top: var(--spacing-2);
|
||||
padding: var(--spacing-3);
|
||||
background-color: var(--theme-color-bg-2);
|
||||
border-radius: var(--border-radius-medium);
|
||||
border: 1px solid var(--theme-color-border-default);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { FeedbackType } from '@noodl-constants/FeedbackType';
|
||||
|
||||
@@ -23,6 +23,13 @@ import { useProjectOrganization } from '../../hooks/useProjectOrganization';
|
||||
import { TagPill, TagPillSize } from '../TagPill';
|
||||
import css from './LauncherProjectCard.module.scss';
|
||||
|
||||
// Runtime version detection types
|
||||
export interface RuntimeVersionInfo {
|
||||
version: 'react17' | 'react19' | 'unknown';
|
||||
confidence: 'high' | 'medium' | 'low';
|
||||
indicators: string[];
|
||||
}
|
||||
|
||||
// FIXME: Use the timeSince function from the editor package when this is moved there
|
||||
function timeSince(date: Date | number) {
|
||||
const date_unix = typeof date === 'number' ? date : date.getTime();
|
||||
@@ -71,11 +78,15 @@ export interface LauncherProjectData {
|
||||
uncommittedChangesAmount?: number;
|
||||
imageSrc: string;
|
||||
contributors?: UserBadgeProps[];
|
||||
runtimeInfo?: RuntimeVersionInfo;
|
||||
}
|
||||
|
||||
export interface LauncherProjectCardProps extends LauncherProjectData {
|
||||
contextMenuItems: ContextMenuProps[];
|
||||
onClick?: () => void;
|
||||
runtimeInfo?: RuntimeVersionInfo;
|
||||
onMigrateProject?: () => void;
|
||||
onOpenReadOnly?: () => void;
|
||||
}
|
||||
|
||||
export function LauncherProjectCard({
|
||||
@@ -90,25 +101,54 @@ export function LauncherProjectCard({
|
||||
imageSrc,
|
||||
contextMenuItems,
|
||||
contributors,
|
||||
onClick
|
||||
onClick,
|
||||
runtimeInfo,
|
||||
onMigrateProject,
|
||||
onOpenReadOnly
|
||||
}: LauncherProjectCardProps) {
|
||||
const { tags, getProjectMeta } = useProjectOrganization();
|
||||
const [showLegacyDetails, setShowLegacyDetails] = useState(false);
|
||||
|
||||
// Get project tags
|
||||
const projectMeta = getProjectMeta(localPath);
|
||||
const projectTags = projectMeta ? tags.filter((tag) => projectMeta.tagIds.includes(tag.id)) : [];
|
||||
|
||||
// Determine if this is a legacy project
|
||||
const isLegacy = runtimeInfo?.version === 'react17';
|
||||
const isDetecting = runtimeInfo === undefined;
|
||||
|
||||
return (
|
||||
<Card background={CardBackground.Bg2} hoverBackground={CardBackground.Bg3} onClick={onClick}>
|
||||
<Card
|
||||
background={CardBackground.Bg2}
|
||||
hoverBackground={CardBackground.Bg3}
|
||||
onClick={
|
||||
isLegacy
|
||||
? () => {
|
||||
// Auto-expand details when user clicks legacy project
|
||||
setShowLegacyDetails(true);
|
||||
}
|
||||
: onClick
|
||||
}
|
||||
UNSAFE_className={isLegacy ? css.LegacyCard : undefined}
|
||||
>
|
||||
<Stack direction="row">
|
||||
<div className={css.Image} style={{ backgroundImage: `url(${imageSrc})` }} />
|
||||
|
||||
<div className={css.Details}>
|
||||
<Columns layoutString="1 1 1" hasXGap={4}>
|
||||
<div>
|
||||
<Title hasBottomSpacing size={TitleSize.Medium}>
|
||||
{title}
|
||||
</Title>
|
||||
<HStack hasSpacing={2} UNSAFE_style={{ alignItems: 'center' }}>
|
||||
<Title hasBottomSpacing size={TitleSize.Medium}>
|
||||
{title}
|
||||
</Title>
|
||||
|
||||
{/* Legacy warning icon */}
|
||||
{isLegacy && (
|
||||
<Tooltip content="This project uses React 17 and needs migration">
|
||||
<Icon icon={IconName.WarningCircle} variant={FeedbackType.Danger} size={IconSize.Default} />
|
||||
</Tooltip>
|
||||
)}
|
||||
</HStack>
|
||||
|
||||
{/* Tags */}
|
||||
{projectTags.length > 0 && (
|
||||
@@ -219,6 +259,66 @@ export function LauncherProjectCard({
|
||||
)}
|
||||
</HStack>
|
||||
</Columns>
|
||||
|
||||
{/* Legacy warning banner */}
|
||||
{isLegacy && (
|
||||
<div className={css.LegacyBanner}>
|
||||
<HStack hasSpacing={2} UNSAFE_style={{ alignItems: 'center', flex: 1 }}>
|
||||
<Icon icon={IconName.WarningCircle} variant={FeedbackType.Danger} size={IconSize.Small} />
|
||||
<Text size={TextSize.Small}>React 17 (Legacy Runtime)</Text>
|
||||
</HStack>
|
||||
|
||||
<TextButton
|
||||
label={showLegacyDetails ? 'Less' : 'Options'}
|
||||
size={TextButtonSize.Small}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setShowLegacyDetails(!showLegacyDetails);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Expanded legacy details */}
|
||||
{isLegacy && showLegacyDetails && (
|
||||
<div className={css.LegacyDetails}>
|
||||
<Label variant={TextType.Shy} size={LabelSize.Default}>
|
||||
This project needs migration to work with OpenNoodl 1.2+. Your original project will remain untouched.
|
||||
</Label>
|
||||
|
||||
<HStack hasSpacing={2} UNSAFE_style={{ marginTop: 'var(--spacing-3)' }}>
|
||||
<PrimaryButton
|
||||
label="Migrate Project"
|
||||
size={PrimaryButtonSize.Small}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onMigrateProject?.();
|
||||
}}
|
||||
/>
|
||||
|
||||
<PrimaryButton
|
||||
label="View Read-Only"
|
||||
size={PrimaryButtonSize.Small}
|
||||
variant={PrimaryButtonVariant.Muted}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onOpenReadOnly?.();
|
||||
}}
|
||||
/>
|
||||
|
||||
<TextButton
|
||||
label="Learn More"
|
||||
size={TextButtonSize.Small}
|
||||
icon={IconName.ExternalLink}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
// TODO: Open documentation
|
||||
window.open('https://docs.opennoodl.com/migration', '_blank');
|
||||
}}
|
||||
/>
|
||||
</HStack>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Stack>
|
||||
</Card>
|
||||
|
||||
@@ -36,7 +36,9 @@ export function Projects({}: ProjectsViewProps) {
|
||||
onOpenProject,
|
||||
onLaunchProject,
|
||||
onOpenProjectFolder,
|
||||
onDeleteProject
|
||||
onDeleteProject,
|
||||
onMigrateProject,
|
||||
onOpenReadOnly
|
||||
} = useLauncherContext();
|
||||
|
||||
const { getProjectMeta, getProjectsInFolder, folders, moveProjectToFolder } = useProjectOrganization();
|
||||
@@ -189,6 +191,8 @@ export function Projects({}: ProjectsViewProps) {
|
||||
key={project.id}
|
||||
{...project}
|
||||
onClick={() => onLaunchProject?.(project.id)}
|
||||
onMigrateProject={() => onMigrateProject?.(project.id)}
|
||||
onOpenReadOnly={() => onOpenReadOnly?.(project.id)}
|
||||
contextMenuItems={[
|
||||
{
|
||||
label: 'Launch project',
|
||||
|
||||
Reference in New Issue
Block a user