Comprehensive codebase audit and cleanup
- Fixed broken test suite: replaced counter widget tests with Console UI tests - Removed dead code: deleted unused MyHomePage widget and svrnty_components.dart library - Updated project branding: renamed package from "my_app" to "console" - Enhanced documentation: rewrote README with project features and setup instructions - Added coding standards: strict typing rules forbidding 'any' type across all languages - Implemented response protocol: structured answer format with context persistence - Fixed backend button: corrected URL from https to http, added proper error handling - Improved .gitignore: added Flutter plugins, CocoaPods, and design folder exclusions - Fixed UI overflow: increased status card height to prevent RenderFlex overflow These changes eliminate technical debt, establish code quality standards, and ensure all functionality works correctly across platforms. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
e23969c3e4
commit
13c963575d
99
.claude_docs/response-protocol.md
Normal file
99
.claude_docs/response-protocol.md
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# MANDATORY RESPONSE PROTOCOL
|
||||||
|
|
||||||
|
**Claude must strictly follow this protocol for ALL responses in this project.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗣️ Response Protocol — Defined Answer Types
|
||||||
|
|
||||||
|
Claude must **always** end responses with exactly one of these two structured formats:
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Answer Type 1: Binary Choice**
|
||||||
|
Used for: simple confirmations, proceed/cancel actions, file operations.
|
||||||
|
|
||||||
|
**Format:**
|
||||||
|
|
||||||
|
(Y) Yes — [brief action summary]
|
||||||
|
|
||||||
|
(N) No — [brief alternative/reason]
|
||||||
|
|
||||||
|
(+) I don't understand — ask for clarification
|
||||||
|
|
||||||
|
|
||||||
|
**When user selects `(+)`:**
|
||||||
|
Claude responds:
|
||||||
|
> "What part would you like me to explain?"
|
||||||
|
Then teaches the concept step‑by‑step in plain language.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Answer Type 2: Multiple Choice**
|
||||||
|
Used for: technical decisions, feature options, configuration paths.
|
||||||
|
|
||||||
|
**Format:**
|
||||||
|
|
||||||
|
(A) Option A — [minimalist description]
|
||||||
|
|
||||||
|
(B) Option B — [minimalist description]
|
||||||
|
|
||||||
|
(C) Option C — [minimalist description]
|
||||||
|
|
||||||
|
(D) Option D — [minimalist description]
|
||||||
|
|
||||||
|
(+) I don't understand — ask for clarification
|
||||||
|
|
||||||
|
|
||||||
|
**When user selects `(+)`:**
|
||||||
|
Claude responds:
|
||||||
|
> "Which option would you like explained, or should I clarify what we're deciding here?"
|
||||||
|
Then provides context on the decision + explains each option's purpose.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ⚠️ Mandatory Rules
|
||||||
|
1. **No text after the last option** — choices must be the final content.
|
||||||
|
2. Every option description ≤8 words.
|
||||||
|
3. The `(+)` option is **always present** in both formats.
|
||||||
|
4. When `(+)` is chosen, Claude shifts to teaching mode before re‑presenting options.
|
||||||
|
5. Claude must include `(always read claude.md to keep context between interactions)` before every option set.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Example 1 (Binary)
|
||||||
|
|
||||||
|
We need to initialize npm in your project folder.
|
||||||
|
|
||||||
|
(always read claude.md to keep context between interactions)
|
||||||
|
|
||||||
|
(Y) Yes — run npm init -y now
|
||||||
|
|
||||||
|
(N) No — show me what this does first
|
||||||
|
|
||||||
|
(+) I don't understand — explain npm initialization
|
||||||
|
|
||||||
|
|
||||||
|
### Example 2 (Multiple Choice)
|
||||||
|
|
||||||
|
Choose your testing framework:
|
||||||
|
|
||||||
|
(always read claude.md to keep context between interactions)
|
||||||
|
|
||||||
|
(A) Jest — popular, feature-rich
|
||||||
|
|
||||||
|
(B) Vitest — faster, Vite-native
|
||||||
|
|
||||||
|
(C) Node test runner — built-in, minimal
|
||||||
|
|
||||||
|
(D) Skip tests — add later
|
||||||
|
|
||||||
|
(+) I don't understand — explain testing frameworks
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**This protocol ensures:**
|
||||||
|
- You always have an escape hatch to learn.
|
||||||
|
- Claude never assumes your technical knowledge.
|
||||||
|
- Every interaction has clear, actionable paths.
|
||||||
8
.gitignore
vendored
8
.gitignore
vendored
@ -27,12 +27,17 @@ migrate_working_dir/
|
|||||||
**/doc/api/
|
**/doc/api/
|
||||||
**/ios/Flutter/.last_build_id
|
**/ios/Flutter/.last_build_id
|
||||||
.dart_tool/
|
.dart_tool/
|
||||||
|
.flutter-plugins
|
||||||
.flutter-plugins-dependencies
|
.flutter-plugins-dependencies
|
||||||
.pub-cache/
|
.pub-cache/
|
||||||
.pub/
|
.pub/
|
||||||
/build/
|
/build/
|
||||||
/coverage/
|
/coverage/
|
||||||
|
|
||||||
|
# CocoaPods
|
||||||
|
**/Pods/
|
||||||
|
**/Podfile.lock
|
||||||
|
|
||||||
# Symbolication related
|
# Symbolication related
|
||||||
app.*.symbols
|
app.*.symbols
|
||||||
|
|
||||||
@ -43,3 +48,6 @@ app.*.map.json
|
|||||||
/android/app/debug
|
/android/app/debug
|
||||||
/android/app/profile
|
/android/app/profile
|
||||||
/android/app/release
|
/android/app/release
|
||||||
|
|
||||||
|
# Design/Documentation folders with duplicate assets
|
||||||
|
Svrnty_norms_guide/
|
||||||
|
|||||||
79
README.md
79
README.md
@ -1,16 +1,77 @@
|
|||||||
# my_app
|
# Svrnty Console
|
||||||
|
|
||||||
A new Flutter project.
|
**Sovereign AI Solutions - Control Panel**
|
||||||
|
|
||||||
|
A Flutter-based management console for the Svrnty AI platform, providing a modern interface for monitoring, configuring, and controlling AI agents and infrastructure.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Dashboard**: Real-time status monitoring of backend services, agents, and system health
|
||||||
|
- **The Architech**: AI infrastructure design and visualization (coming soon)
|
||||||
|
- **Agent Management**: Configure and monitor AI agents
|
||||||
|
- **Analytics**: Metrics and performance monitoring
|
||||||
|
- **Dark Theme**: Professional dark mode with Svrnty brand colors
|
||||||
|
|
||||||
|
## Tech Stack
|
||||||
|
|
||||||
|
- **Flutter 3.x** - Cross-platform UI framework
|
||||||
|
- **GetWidget** - Modern UI component library
|
||||||
|
- **Iconsax** - Clean, modern icon set
|
||||||
|
- **Animate Do** - Smooth animations
|
||||||
|
- **Custom Theming** - Svrnty brand colors (Crimson Red #C44D58, Slate Blue #475C6C)
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
lib/
|
||||||
|
├── main.dart # App entry point
|
||||||
|
├── console_landing_page.dart # Main console UI
|
||||||
|
├── theme.dart # Material theme configuration
|
||||||
|
├── components/
|
||||||
|
│ └── navigation_sidebar.dart # Collapsible navigation
|
||||||
|
└── pages/
|
||||||
|
└── architech_page.dart # The Architech module
|
||||||
|
```
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
This project is a starting point for a Flutter application.
|
### Prerequisites
|
||||||
|
|
||||||
A few resources to get you started if this is your first Flutter project:
|
- Flutter SDK 3.9.2 or higher
|
||||||
|
- Dart SDK 3.9.2 or higher
|
||||||
|
|
||||||
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
### Installation
|
||||||
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
|
||||||
|
|
||||||
For help getting started with Flutter development, view the
|
```bash
|
||||||
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
# Clone the repository
|
||||||
samples, guidance on mobile development, and a full API reference.
|
git clone [repository-url]
|
||||||
|
cd Console
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
flutter pub get
|
||||||
|
|
||||||
|
# Run the application
|
||||||
|
flutter run
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run tests
|
||||||
|
flutter test
|
||||||
|
|
||||||
|
# Analyze code
|
||||||
|
flutter analyze
|
||||||
|
|
||||||
|
# Build for production
|
||||||
|
flutter build macos # or ios, web, etc.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Brand Fonts
|
||||||
|
|
||||||
|
- **Montserrat** - Primary UI font
|
||||||
|
- **IBM Plex Mono** - Code and technical content
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Private - Svrnty AI Solutions
|
||||||
|
|||||||
139
claude.md
Normal file
139
claude.md
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
# 🔒 MANDATORY CODING STANDARDS
|
||||||
|
|
||||||
|
## Strict Typing - NO EXCEPTIONS
|
||||||
|
|
||||||
|
**Claude must ALWAYS use explicit types in ALL code. The use of `any` type is FORBIDDEN.**
|
||||||
|
|
||||||
|
### Rules:
|
||||||
|
1. **Every variable must have an explicit type annotation**
|
||||||
|
2. **Every function parameter must be typed**
|
||||||
|
3. **Every function return value must be typed**
|
||||||
|
4. **Never use `any`, `dynamic` (in Dart), or equivalent loose types**
|
||||||
|
5. **Use proper generics, interfaces, and type unions instead**
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
|
||||||
|
❌ **FORBIDDEN:**
|
||||||
|
```typescript
|
||||||
|
const data: any = fetchData();
|
||||||
|
function process(input: any): any { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
```dart
|
||||||
|
dynamic value = getValue();
|
||||||
|
void handleData(var data) { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **REQUIRED:**
|
||||||
|
```typescript
|
||||||
|
const data: UserData = fetchData();
|
||||||
|
function process(input: UserInput): ProcessedOutput { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
```dart
|
||||||
|
UserData value = getValue();
|
||||||
|
void handleData(RequestData data) { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
**This rule applies to:**
|
||||||
|
- TypeScript/JavaScript
|
||||||
|
- Dart/Flutter
|
||||||
|
- Python (use type hints)
|
||||||
|
- All statically-typed languages
|
||||||
|
- Even when interfacing with external APIs - create proper type definitions
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗣️ Response Protocol — Defined Answer Types
|
||||||
|
|
||||||
|
Claude must **always** end responses with exactly one of these two structured formats:
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Answer Type 1: Binary Choice**
|
||||||
|
Used for: simple confirmations, proceed/cancel actions, file operations.
|
||||||
|
|
||||||
|
**Format:**
|
||||||
|
|
||||||
|
(Y) Yes — [brief action summary]
|
||||||
|
|
||||||
|
(N) No — [brief alternative/reason]
|
||||||
|
|
||||||
|
(+) I don't understand — ask for clarification
|
||||||
|
|
||||||
|
|
||||||
|
**When user selects `(+)`:**
|
||||||
|
Claude responds:
|
||||||
|
> "What part would you like me to explain?"
|
||||||
|
Then teaches the concept step‑by‑step in plain language.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Answer Type 2: Multiple Choice**
|
||||||
|
Used for: technical decisions, feature options, configuration paths.
|
||||||
|
|
||||||
|
**Format:**
|
||||||
|
|
||||||
|
(A) Option A — [minimalist description]
|
||||||
|
|
||||||
|
(B) Option B — [minimalist description]
|
||||||
|
|
||||||
|
(C) Option C — [minimalist description]
|
||||||
|
|
||||||
|
(D) Option D — [minimalist description]
|
||||||
|
|
||||||
|
(+) I don't understand — ask for clarification
|
||||||
|
|
||||||
|
|
||||||
|
**When user selects `(+)`:**
|
||||||
|
Claude responds:
|
||||||
|
> "Which option would you like explained, or should I clarify what we're deciding here?"
|
||||||
|
Then provides context on the decision + explains each option's purpose.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ⚠️ Mandatory Rules
|
||||||
|
1. **No text after the last option** — choices must be the final content.
|
||||||
|
2. Every option description ≤8 words.
|
||||||
|
3. The `(+)` option is **always present** in both formats.
|
||||||
|
4. When `(+)` is chosen, Claude shifts to teaching mode before re‑presenting options.
|
||||||
|
5. Claude must include `(always read claude.md to keep context between interactions)` before every option set.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Example 1 (Binary)
|
||||||
|
|
||||||
|
We need to initialize npm in your project folder.
|
||||||
|
|
||||||
|
(always read claude.md to keep context between interactions)
|
||||||
|
|
||||||
|
(Y) Yes — run npm init -y now
|
||||||
|
|
||||||
|
(N) No — show me what this does first
|
||||||
|
|
||||||
|
(+) I don't understand — explain npm initialization
|
||||||
|
|
||||||
|
|
||||||
|
### Example 2 (Multiple Choice)
|
||||||
|
|
||||||
|
Choose your testing framework:
|
||||||
|
|
||||||
|
(always read claude.md to keep context between interactions)
|
||||||
|
|
||||||
|
(A) Jest — popular, feature-rich
|
||||||
|
|
||||||
|
(B) Vitest — faster, Vite-native
|
||||||
|
|
||||||
|
(C) Node test runner — built-in, minimal
|
||||||
|
|
||||||
|
(D) Skip tests — add later
|
||||||
|
|
||||||
|
(+) I don't understand — explain testing frameworks
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**This protocol ensures:**
|
||||||
|
- You always have an escape hatch to learn.
|
||||||
|
- Claude never assumes your technical knowledge.
|
||||||
|
- Every interaction has clear, actionable paths.
|
||||||
43
ios/Podfile
Normal file
43
ios/Podfile
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Uncomment this line to define a global platform for your project
|
||||||
|
# platform :ios, '13.0'
|
||||||
|
|
||||||
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||||
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||||
|
|
||||||
|
project 'Runner', {
|
||||||
|
'Debug' => :debug,
|
||||||
|
'Profile' => :release,
|
||||||
|
'Release' => :release,
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutter_root
|
||||||
|
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
|
||||||
|
unless File.exist?(generated_xcode_build_settings_path)
|
||||||
|
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
|
||||||
|
end
|
||||||
|
|
||||||
|
File.foreach(generated_xcode_build_settings_path) do |line|
|
||||||
|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
||||||
|
return matches[1].strip if matches
|
||||||
|
end
|
||||||
|
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
|
||||||
|
end
|
||||||
|
|
||||||
|
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
|
||||||
|
|
||||||
|
flutter_ios_podfile_setup
|
||||||
|
|
||||||
|
target 'Runner' do
|
||||||
|
use_frameworks!
|
||||||
|
|
||||||
|
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
||||||
|
target 'RunnerTests' do
|
||||||
|
inherit! :search_paths
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
post_install do |installer|
|
||||||
|
installer.pods_project.targets.each do |target|
|
||||||
|
flutter_additional_ios_build_settings(target)
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -1,478 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:animate_do/animate_do.dart';
|
|
||||||
|
|
||||||
/// Svrnty Design System Components
|
|
||||||
/// Reusable, branded components for the Svrnty Console application
|
|
||||||
///
|
|
||||||
/// Brand Colors:
|
|
||||||
/// - Primary (Crimson): #C44D58
|
|
||||||
/// - Secondary (Slate Blue): #475C6C
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// SVRNTY BUTTONS
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
enum SvrntyButtonVariant { primary, secondary, ghost, danger }
|
|
||||||
|
|
||||||
class SvrntyButton extends StatelessWidget {
|
|
||||||
final String text;
|
|
||||||
final VoidCallback? onPressed;
|
|
||||||
final SvrntyButtonVariant variant;
|
|
||||||
final IconData? icon;
|
|
||||||
final bool isLoading;
|
|
||||||
final bool fullWidth;
|
|
||||||
|
|
||||||
const SvrntyButton({
|
|
||||||
Key? key,
|
|
||||||
required this.text,
|
|
||||||
this.onPressed,
|
|
||||||
this.variant = SvrntyButtonVariant.primary,
|
|
||||||
this.icon,
|
|
||||||
this.isLoading = false,
|
|
||||||
this.fullWidth = false,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
|
||||||
|
|
||||||
Color backgroundColor;
|
|
||||||
Color textColor;
|
|
||||||
Color? borderColor;
|
|
||||||
|
|
||||||
switch (variant) {
|
|
||||||
case SvrntyButtonVariant.primary:
|
|
||||||
backgroundColor = colorScheme.primary;
|
|
||||||
textColor = Colors.white;
|
|
||||||
borderColor = null;
|
|
||||||
break;
|
|
||||||
case SvrntyButtonVariant.secondary:
|
|
||||||
backgroundColor = colorScheme.secondary;
|
|
||||||
textColor = Colors.white;
|
|
||||||
borderColor = null;
|
|
||||||
break;
|
|
||||||
case SvrntyButtonVariant.ghost:
|
|
||||||
backgroundColor = Colors.transparent;
|
|
||||||
textColor = colorScheme.primary;
|
|
||||||
borderColor = colorScheme.primary;
|
|
||||||
break;
|
|
||||||
case SvrntyButtonVariant.danger:
|
|
||||||
backgroundColor = colorScheme.error;
|
|
||||||
textColor = Colors.white;
|
|
||||||
borderColor = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FadeInUp(
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
child: SizedBox(
|
|
||||||
width: fullWidth ? double.infinity : null,
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: isLoading ? null : onPressed,
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: backgroundColor,
|
|
||||||
foregroundColor: textColor,
|
|
||||||
elevation: variant == SvrntyButtonVariant.ghost ? 0 : 2,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
side: borderColor != null
|
|
||||||
? BorderSide(color: borderColor, width: 2)
|
|
||||||
: BorderSide.none,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: isLoading
|
|
||||||
? SizedBox(
|
|
||||||
height: 20,
|
|
||||||
width: 20,
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
strokeWidth: 2,
|
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(textColor),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
if (icon != null) ...[
|
|
||||||
Icon(icon, size: 20),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
],
|
|
||||||
Text(
|
|
||||||
text,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// SVRNTY CARDS
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
class SvrntyCard extends StatelessWidget {
|
|
||||||
final Widget child;
|
|
||||||
final Color? accentColor;
|
|
||||||
final VoidCallback? onTap;
|
|
||||||
final bool showBorder;
|
|
||||||
final EdgeInsetsGeometry? padding;
|
|
||||||
|
|
||||||
const SvrntyCard({
|
|
||||||
Key? key,
|
|
||||||
required this.child,
|
|
||||||
this.accentColor,
|
|
||||||
this.onTap,
|
|
||||||
this.showBorder = true,
|
|
||||||
this.padding,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
|
||||||
final effectiveAccentColor = accentColor ?? colorScheme.primary;
|
|
||||||
|
|
||||||
return FadeInUp(
|
|
||||||
duration: const Duration(milliseconds: 400),
|
|
||||||
child: Container(
|
|
||||||
decoration: showBorder
|
|
||||||
? BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
gradient: LinearGradient(
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
colors: [
|
|
||||||
effectiveAccentColor.withOpacity(0.15),
|
|
||||||
effectiveAccentColor.withOpacity(0.05),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
border: Border.all(
|
|
||||||
color: effectiveAccentColor.withOpacity(0.3),
|
|
||||||
width: 1,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
child: Card(
|
|
||||||
elevation: 2,
|
|
||||||
color: showBorder ? Colors.transparent : null,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
child: InkWell(
|
|
||||||
onTap: onTap,
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
child: Container(
|
|
||||||
decoration: showBorder
|
|
||||||
? BoxDecoration(
|
|
||||||
border: Border(
|
|
||||||
left: BorderSide(
|
|
||||||
color: effectiveAccentColor,
|
|
||||||
width: 4,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
child: Padding(
|
|
||||||
padding: padding ?? const EdgeInsets.all(20),
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// SVRNTY BADGES
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
enum SvrntyBadgeStatus { success, warning, error, info, neutral }
|
|
||||||
|
|
||||||
class SvrntyBadge extends StatelessWidget {
|
|
||||||
final String text;
|
|
||||||
final SvrntyBadgeStatus status;
|
|
||||||
final IconData? icon;
|
|
||||||
|
|
||||||
const SvrntyBadge({
|
|
||||||
Key? key,
|
|
||||||
required this.text,
|
|
||||||
this.status = SvrntyBadgeStatus.neutral,
|
|
||||||
this.icon,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
|
||||||
|
|
||||||
Color backgroundColor;
|
|
||||||
Color textColor;
|
|
||||||
|
|
||||||
switch (status) {
|
|
||||||
case SvrntyBadgeStatus.success:
|
|
||||||
backgroundColor = Colors.green;
|
|
||||||
textColor = Colors.white;
|
|
||||||
break;
|
|
||||||
case SvrntyBadgeStatus.warning:
|
|
||||||
backgroundColor = Colors.orange;
|
|
||||||
textColor = Colors.white;
|
|
||||||
break;
|
|
||||||
case SvrntyBadgeStatus.error:
|
|
||||||
backgroundColor = colorScheme.error;
|
|
||||||
textColor = Colors.white;
|
|
||||||
break;
|
|
||||||
case SvrntyBadgeStatus.info:
|
|
||||||
backgroundColor = colorScheme.primary;
|
|
||||||
textColor = Colors.white;
|
|
||||||
break;
|
|
||||||
case SvrntyBadgeStatus.neutral:
|
|
||||||
backgroundColor = colorScheme.secondary;
|
|
||||||
textColor = Colors.white;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Pulse(
|
|
||||||
duration: const Duration(milliseconds: 1000),
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: backgroundColor,
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: backgroundColor.withOpacity(0.3),
|
|
||||||
blurRadius: 8,
|
|
||||||
offset: const Offset(0, 2),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
if (icon != null) ...[
|
|
||||||
Icon(icon, color: textColor, size: 16),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
],
|
|
||||||
Text(
|
|
||||||
text,
|
|
||||||
style: TextStyle(
|
|
||||||
color: textColor,
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontFamily: 'Montserrat',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// SVRNTY ICON BUTTONS
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
class SvrntyIconButton extends StatelessWidget {
|
|
||||||
final IconData icon;
|
|
||||||
final VoidCallback? onPressed;
|
|
||||||
final Color? backgroundColor;
|
|
||||||
final Color? iconColor;
|
|
||||||
final double size;
|
|
||||||
|
|
||||||
const SvrntyIconButton({
|
|
||||||
Key? key,
|
|
||||||
required this.icon,
|
|
||||||
this.onPressed,
|
|
||||||
this.backgroundColor,
|
|
||||||
this.iconColor,
|
|
||||||
this.size = 40,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
|
||||||
|
|
||||||
return FadeIn(
|
|
||||||
child: Container(
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: backgroundColor ?? colorScheme.primary.withOpacity(0.1),
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: IconButton(
|
|
||||||
icon: Icon(icon),
|
|
||||||
color: iconColor ?? colorScheme.primary,
|
|
||||||
iconSize: size * 0.5,
|
|
||||||
onPressed: onPressed,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// SVRNTY SECTION HEADER
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
class SvrntySectionHeader extends StatelessWidget {
|
|
||||||
final String title;
|
|
||||||
final String? subtitle;
|
|
||||||
final Widget? action;
|
|
||||||
|
|
||||||
const SvrntySectionHeader({
|
|
||||||
Key? key,
|
|
||||||
required this.title,
|
|
||||||
this.subtitle,
|
|
||||||
this.action,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
|
||||||
|
|
||||||
return FadeInLeft(
|
|
||||||
duration: const Duration(milliseconds: 400),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 4,
|
|
||||||
height: 28,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: colorScheme.primary,
|
|
||||||
borderRadius: BorderRadius.circular(2),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
title,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: colorScheme.onSurface,
|
|
||||||
fontFamily: 'Montserrat',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (subtitle != null) ...[
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
subtitle!,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: colorScheme.onSurfaceVariant,
|
|
||||||
fontFamily: 'Montserrat',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (action != null) action!,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// SVRNTY INPUT FIELD
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
class SvrntyTextField extends StatelessWidget {
|
|
||||||
final String label;
|
|
||||||
final String? hint;
|
|
||||||
final TextEditingController? controller;
|
|
||||||
final IconData? prefixIcon;
|
|
||||||
final bool obscureText;
|
|
||||||
final TextInputType? keyboardType;
|
|
||||||
final String? Function(String?)? validator;
|
|
||||||
|
|
||||||
const SvrntyTextField({
|
|
||||||
Key? key,
|
|
||||||
required this.label,
|
|
||||||
this.hint,
|
|
||||||
this.controller,
|
|
||||||
this.prefixIcon,
|
|
||||||
this.obscureText = false,
|
|
||||||
this.keyboardType,
|
|
||||||
this.validator,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
|
||||||
|
|
||||||
return FadeInUp(
|
|
||||||
duration: const Duration(milliseconds: 350),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
label,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: colorScheme.onSurface,
|
|
||||||
fontFamily: 'Montserrat',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
TextFormField(
|
|
||||||
controller: controller,
|
|
||||||
obscureText: obscureText,
|
|
||||||
keyboardType: keyboardType,
|
|
||||||
validator: validator,
|
|
||||||
style: const TextStyle(fontFamily: 'Montserrat'),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: hint,
|
|
||||||
prefixIcon: prefixIcon != null ? Icon(prefixIcon) : null,
|
|
||||||
filled: true,
|
|
||||||
fillColor: colorScheme.surfaceContainerHighest,
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: colorScheme.outline.withOpacity(0.3),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: colorScheme.outline.withOpacity(0.2),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: colorScheme.primary,
|
|
||||||
width: 2,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
errorBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: colorScheme.error,
|
|
||||||
width: 2,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 16,
|
|
||||||
vertical: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -2,9 +2,9 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:iconsax/iconsax.dart';
|
import 'package:iconsax/iconsax.dart';
|
||||||
import 'package:animate_do/animate_do.dart';
|
import 'package:animate_do/animate_do.dart';
|
||||||
import 'package:getwidget/getwidget.dart';
|
import 'package:getwidget/getwidget.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'components/navigation_sidebar.dart';
|
import 'components/navigation_sidebar.dart';
|
||||||
import 'pages/architech_page.dart';
|
import 'pages/architech_page.dart';
|
||||||
import 'dart:html' as html;
|
|
||||||
|
|
||||||
class ConsoleLandingPage extends StatefulWidget {
|
class ConsoleLandingPage extends StatefulWidget {
|
||||||
const ConsoleLandingPage({Key? key}) : super(key: key);
|
const ConsoleLandingPage({Key? key}) : super(key: key);
|
||||||
@ -195,7 +195,7 @@ class _ConsoleLandingPageState extends State<ConsoleLandingPage> {
|
|||||||
colorScheme.primary,
|
colorScheme.primary,
|
||||||
Icons.api,
|
Icons.api,
|
||||||
'Active',
|
'Active',
|
||||||
url: 'https://localhost:7108/swagger/',
|
url: 'http://localhost:7108/swagger/',
|
||||||
),
|
),
|
||||||
_buildStatusCard(
|
_buildStatusCard(
|
||||||
'Frontend',
|
'Frontend',
|
||||||
@ -306,7 +306,7 @@ class _ConsoleLandingPageState extends State<ConsoleLandingPage> {
|
|||||||
return GridView.builder(
|
return GridView.builder(
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: crossAxisCount,
|
crossAxisCount: crossAxisCount,
|
||||||
mainAxisExtent: 140,
|
mainAxisExtent: 155,
|
||||||
crossAxisSpacing: spacing,
|
crossAxisSpacing: spacing,
|
||||||
mainAxisSpacing: spacing,
|
mainAxisSpacing: spacing,
|
||||||
),
|
),
|
||||||
@ -353,9 +353,46 @@ class _ConsoleLandingPageState extends State<ConsoleLandingPage> {
|
|||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
),
|
),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () async {
|
||||||
if (url != null) {
|
if (url != null) {
|
||||||
html.window.open(url, '_blank');
|
try {
|
||||||
|
final uri = Uri.parse(url);
|
||||||
|
if (await canLaunchUrl(uri)) {
|
||||||
|
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||||
|
if (context.mounted) {
|
||||||
|
GFToast.showToast(
|
||||||
|
'Opening $title...',
|
||||||
|
context,
|
||||||
|
toastPosition: GFToastPosition.BOTTOM,
|
||||||
|
textStyle: const TextStyle(fontSize: 14, color: Colors.white),
|
||||||
|
backgroundColor: color.withOpacity(0.9),
|
||||||
|
toastDuration: 2,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (context.mounted) {
|
||||||
|
GFToast.showToast(
|
||||||
|
'Cannot open $url',
|
||||||
|
context,
|
||||||
|
toastPosition: GFToastPosition.BOTTOM,
|
||||||
|
textStyle: const TextStyle(fontSize: 14, color: Colors.white),
|
||||||
|
backgroundColor: colorScheme.error.withOpacity(0.9),
|
||||||
|
toastDuration: 3,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (context.mounted) {
|
||||||
|
GFToast.showToast(
|
||||||
|
'Error: ${e.toString()}',
|
||||||
|
context,
|
||||||
|
toastPosition: GFToastPosition.BOTTOM,
|
||||||
|
textStyle: const TextStyle(fontSize: 14, color: Colors.white),
|
||||||
|
backgroundColor: colorScheme.error.withOpacity(0.9),
|
||||||
|
toastDuration: 3,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
GFToast.showToast(
|
GFToast.showToast(
|
||||||
'$title tapped',
|
'$title tapped',
|
||||||
|
|||||||
@ -22,89 +22,3 @@ class MyApp extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyHomePage extends StatefulWidget {
|
|
||||||
const MyHomePage({super.key, required this.title});
|
|
||||||
|
|
||||||
// This widget is the home page of your application. It is stateful, meaning
|
|
||||||
// that it has a State object (defined below) that contains fields that affect
|
|
||||||
// how it looks.
|
|
||||||
|
|
||||||
// This class is the configuration for the state. It holds the values (in this
|
|
||||||
// case the title) provided by the parent (in this case the App widget) and
|
|
||||||
// used by the build method of the State. Fields in a Widget subclass are
|
|
||||||
// always marked "final".
|
|
||||||
|
|
||||||
final String title;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<MyHomePage> createState() => _MyHomePageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MyHomePageState extends State<MyHomePage> {
|
|
||||||
int _counter = 0;
|
|
||||||
|
|
||||||
void _incrementCounter() {
|
|
||||||
setState(() {
|
|
||||||
// This call to setState tells the Flutter framework that something has
|
|
||||||
// changed in this State, which causes it to rerun the build method below
|
|
||||||
// so that the display can reflect the updated values. If we changed
|
|
||||||
// _counter without calling setState(), then the build method would not be
|
|
||||||
// called again, and so nothing would appear to happen.
|
|
||||||
_counter++;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
// This method is rerun every time setState is called, for instance as done
|
|
||||||
// by the _incrementCounter method above.
|
|
||||||
//
|
|
||||||
// The Flutter framework has been optimized to make rerunning build methods
|
|
||||||
// fast, so that you can just rebuild anything that needs updating rather
|
|
||||||
// than having to individually change instances of widgets.
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
// TRY THIS: Try changing the color here to a specific color (to
|
|
||||||
// Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
|
|
||||||
// change color while the other colors stay the same.
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
|
||||||
// Here we take the value from the MyHomePage object that was created by
|
|
||||||
// the App.build method, and use it to set our appbar title.
|
|
||||||
title: Text(widget.title),
|
|
||||||
),
|
|
||||||
body: Center(
|
|
||||||
// Center is a layout widget. It takes a single child and positions it
|
|
||||||
// in the middle of the parent.
|
|
||||||
child: Column(
|
|
||||||
// Column is also a layout widget. It takes a list of children and
|
|
||||||
// arranges them vertically. By default, it sizes itself to fit its
|
|
||||||
// children horizontally, and tries to be as tall as its parent.
|
|
||||||
//
|
|
||||||
// Column has various properties to control how it sizes itself and
|
|
||||||
// how it positions its children. Here we use mainAxisAlignment to
|
|
||||||
// center the children vertically; the main axis here is the vertical
|
|
||||||
// axis because Columns are vertical (the cross axis would be
|
|
||||||
// horizontal).
|
|
||||||
//
|
|
||||||
// TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
|
|
||||||
// action in the IDE, or press "p" in the console), to see the
|
|
||||||
// wireframe for each widget.
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: <Widget>[
|
|
||||||
const Text('You have pushed the button this many times:'),
|
|
||||||
Text(
|
|
||||||
'$_counter',
|
|
||||||
style: Theme.of(context).textTheme.headlineMedium,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
floatingActionButton: FloatingActionButton(
|
|
||||||
onPressed: _incrementCounter,
|
|
||||||
tooltip: 'Increment',
|
|
||||||
child: const Icon(Icons.add),
|
|
||||||
), // This trailing comma makes auto-formatting nicer for build methods.
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
42
macos/Podfile
Normal file
42
macos/Podfile
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
platform :osx, '10.15'
|
||||||
|
|
||||||
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||||
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||||
|
|
||||||
|
project 'Runner', {
|
||||||
|
'Debug' => :debug,
|
||||||
|
'Profile' => :release,
|
||||||
|
'Release' => :release,
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutter_root
|
||||||
|
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
|
||||||
|
unless File.exist?(generated_xcode_build_settings_path)
|
||||||
|
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
|
||||||
|
end
|
||||||
|
|
||||||
|
File.foreach(generated_xcode_build_settings_path) do |line|
|
||||||
|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
||||||
|
return matches[1].strip if matches
|
||||||
|
end
|
||||||
|
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
|
||||||
|
end
|
||||||
|
|
||||||
|
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
|
||||||
|
|
||||||
|
flutter_macos_podfile_setup
|
||||||
|
|
||||||
|
target 'Runner' do
|
||||||
|
use_frameworks!
|
||||||
|
|
||||||
|
flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
|
||||||
|
target 'RunnerTests' do
|
||||||
|
inherit! :search_paths
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
post_install do |installer|
|
||||||
|
installer.pods_project.targets.each do |target|
|
||||||
|
flutter_additional_macos_build_settings(target)
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -1,5 +1,5 @@
|
|||||||
name: my_app
|
name: console
|
||||||
description: "A new Flutter project."
|
description: "Svrnty Console - Sovereign AI Solutions control panel."
|
||||||
# The following line prevents the package from being accidentally published to
|
# The following line prevents the package from being accidentally published to
|
||||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
@ -41,6 +41,7 @@ dependencies:
|
|||||||
iconsax: ^0.0.8 # Modern clean icons
|
iconsax: ^0.0.8 # Modern clean icons
|
||||||
flutter_animate: ^4.3.0 # Advanced animations
|
flutter_animate: ^4.3.0 # Advanced animations
|
||||||
getwidget: ^7.0.0 # Modern UI component library (compatible with Flutter 3.35.0+)
|
getwidget: ^7.0.0 # Modern UI component library (compatible with Flutter 3.35.0+)
|
||||||
|
url_launcher: ^6.3.1 # Cross-platform URL launching
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
@ -1,30 +1,19 @@
|
|||||||
// This is a basic Flutter widget test.
|
// Svrnty Console Widget Tests
|
||||||
//
|
|
||||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
|
||||||
// utility in the flutter_test package. For example, you can send tap and scroll
|
|
||||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
|
||||||
// tree, read text, and verify that the values of widget properties are correct.
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
import 'package:my_app/main.dart';
|
import 'package:console/main.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
testWidgets('App launches and displays Console UI', (WidgetTester tester) async {
|
||||||
// Build our app and trigger a frame.
|
// Build our app and trigger a frame.
|
||||||
await tester.pumpWidget(const MyApp());
|
await tester.pumpWidget(const MyApp());
|
||||||
|
|
||||||
// Verify that our counter starts at 0.
|
// Verify that the app title is set correctly.
|
||||||
expect(find.text('0'), findsOneWidget);
|
expect(find.text('Svrnty'), findsOneWidget);
|
||||||
expect(find.text('1'), findsNothing);
|
expect(find.text('Console'), findsOneWidget);
|
||||||
|
|
||||||
// Tap the '+' icon and trigger a frame.
|
// Verify dashboard is displayed by default.
|
||||||
await tester.tap(find.byIcon(Icons.add));
|
expect(find.text('Dashboard'), findsWidgets);
|
||||||
await tester.pump();
|
|
||||||
|
|
||||||
// Verify that our counter has incremented.
|
|
||||||
expect(find.text('0'), findsNothing);
|
|
||||||
expect(find.text('1'), findsOneWidget);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user