Add basic app structure with Dashboard, Transactions and Profile views. Create TransactionModel and TransactionRowView

This commit is contained in:
“SamoilenkoVadym” 2025-03-02 18:34:46 +00:00
parent 800494f34e
commit 2ec0711bde
10 changed files with 270 additions and 24 deletions

View file

@ -9,9 +9,12 @@ import SwiftUI
@main
struct CoinlyApp: App {
let persistenceController = PersistenceController.shared
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}

View file

@ -0,0 +1,35 @@
//
// ContentView.swift
// Coinly
//
// Created by Vadym Samoilenko on 02/03/2025.
//
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
DashboardView()
.tabItem {
Label("Dashboard", systemImage: "chart.pie.fill")
}
TransactionsView()
.tabItem {
Label("Transactions", systemImage: "list.bullet")
}
ProfileView()
.tabItem {
Label("Profile", systemImage: "person.fill")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

View file

@ -0,0 +1,29 @@
//
// Persistence.swift
// Coinly
//
// Created by Vadym Samoilenko on 02/03/2025.
//
import CoreData
struct PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "Coinly")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
}
container.viewContext.automaticallyMergesChangesFromParent = true
}
}

View file

@ -1,24 +0,0 @@
//
// ContentView.swift
// Coinly
//
// Created by Vadym Samoilenko on 02/03/2025.
//
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
}
.padding()
}
}
#Preview {
ContentView()
}

View file

@ -0,0 +1,35 @@
//
// DashboardView.swift
// Coinly
//
// Created by Vadym Samoilenko on 02/03/2025.
//
import SwiftUI
struct DashboardView: View {
var body: some View {
NavigationView {
VStack {
Text("Total Balance")
.font(.subheadline)
.foregroundColor(.gray)
Text("$1,234.56")
.font(.title)
.fontWeight(.bold)
Spacer()
}
.padding()
.navigationTitle("Dashboard")
}
}
}
struct DashboardView_Previews: PreviewProvider {
static var previews: some View {
DashboardView()
}
}

View file

@ -0,0 +1,40 @@
//
// TransactionType.swift
// Coinly
//
// Created by Vadym Samoilenko on 02/03/2025.
//
import Foundation
enum TransactionType: String {
case income = "Income"
case expense = "Expense"
}
struct TransactionModel: Identifiable {
let id = UUID()
var amount: Double
var date: Date
var type: TransactionType
var category: String
var note: String?
var isExpense: Bool {
type == .expense
}
var signedAmount: Double {
isExpense ? -amount : amount
}
}
// Sample Data
extension TransactionModel {
static let sampleData = [
TransactionModel(amount: 100, date: Date(), type: .income, category: "Salary", note: "Monthly salary"),
TransactionModel(amount: 25.99, date: Date(), type: .expense, category: "Food", note: "Lunch"),
TransactionModel(amount: 50, date: Date(), type: .expense, category: "Transport", note: "Fuel")
]
}

View file

@ -0,0 +1,36 @@
//
// ProfileView.swift
// Coinly
//
// Created by Vadym Samoilenko on 02/03/2025.
//
import SwiftUI
struct ProfileView: View {
var body: some View {
NavigationView {
List {
Section("Settings") {
Text("Categories")
Text("Currency")
Text("Notifications")
}
Section("About") {
Text("Help")
Text("Privacy Policy")
Text("Version 1.0")
}
}
.navigationTitle("Profile")
}
}
}
struct ProfileView_Previews: PreviewProvider {
static var previews: some View {
ProfileView()
}
}

View file

@ -0,0 +1,35 @@
//
// TransactionsView.swift
// Coinly
//
// Created by Vadym Samoilenko on 02/03/2025.
//
import SwiftUI
struct TransactionsView: View {
var body: some View {
NavigationView {
List {
Text("Transaction 1")
Text("Transaction 2")
Text("Transaction 3")
}
.navigationTitle("Transactions")
.toolbar {
Button(action: {
// Add transaction action
}) {
Image(systemName: "plus")
}
}
}
}
}
struct TransactionsView_Previews: PreviewProvider {
static var previews: some View {
TransactionsView()
}
}

View file

@ -0,0 +1,47 @@
//
// TransactionRowView.swift
// Coinly
//
// Created by Vadym Samoilenko on 02/03/2025.
//
import SwiftUI
struct TransactionRowView: View {
let transaction: TransactionModel
var body: some View {
HStack {
VStack(alignment: .leading) {
Text(transaction.category)
.font(.headline)
if let note = transaction.note {
Text(note)
.font(.subheadline)
.foregroundColor(.gray)
}
}
Spacer()
VStack(alignment: .trailing) {
Text(String(format: "%.2f", transaction.amount))
.font(.headline)
.foregroundColor(transaction.isExpense ? .red : .green)
Text(transaction.date, style: .date)
.font(.caption)
.foregroundColor(.gray)
}
}
.padding(.vertical, 8)
}
}
struct TransactionRowView_Previews: PreviewProvider {
static var previews: some View {
TransactionRowView(transaction: TransactionModel.sampleData[0])
.previewLayout(.sizeThatFits)
.padding()
}
}

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="23605" systemVersion="24D70" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithSwiftData="YES" userDefinedModelVersionIdentifier="">
<entity name="Transaction" representedClassName="Transaction" syncable="YES" codeGenerationType="class">
<attribute name="amount" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="category" optional="YES" attributeType="String"/>
<attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="note" optional="YES" attributeType="String"/>
<attribute name="type" optional="YES" attributeType="String"/>
</entity>
</model>