Kaynağa Gözat

Polish the home dashboard UI with cleaner cards and controls.

Improve top-bar, action tile, and meetings panel styling while wiring the Schedule action to open Zoom scheduling.

Made-with: Cursor
huzaifahayat12 3 ay önce
ebeveyn
işleme
b85cc18710
1 değiştirilmiş dosya ile 93 ekleme ve 46 silme
  1. 93 46
      zoom_app/ViewController.swift

+ 93 - 46
zoom_app/ViewController.swift

@@ -165,6 +165,14 @@ class ViewController: NSViewController {
         }
     }
 
+    @objc private func scheduleMeetingWebTapped() {
+        guard let url = URL(string: "https://zoom.us/meeting/schedule") else { return }
+        let opened = NSWorkspace.shared.open(url)
+        if opened == false {
+            meetingsStatusLabel?.stringValue = "Unable to open Zoom schedule page."
+        }
+    }
+
     @MainActor
     private func resetLoginSigningInState() {
         isSigningIn = false
@@ -533,36 +541,50 @@ class ViewController: NSViewController {
         let topBar = NSView()
         topBar.wantsLayer = true
         topBar.layer?.backgroundColor = cardBackground.cgColor
-        topBar.layer?.cornerRadius = 10
-        let search = makeLabel("Search (\u{2318}E)", size: 17, color: mutedText, weight: .regular, centered: true)
-        let name = makeLabel(profile?.name ?? "User", size: 14, color: primaryText, weight: .medium, centered: false)
-        name.wantsLayer = true
-        name.layer?.backgroundColor = accentBlue.withAlphaComponent(0.28).cgColor
-        name.layer?.cornerRadius = 10
-
-        let timeTitle = makeLabel("--:--", size: 54, color: primaryText, weight: .bold, centered: true)
-        let dateTitle = makeLabel("-", size: 18, color: secondaryText, weight: .regular, centered: true)
+        topBar.layer?.cornerRadius = 12
+        topBar.layer?.borderWidth = 1
+        topBar.layer?.borderColor = NSColor.white.withAlphaComponent(0.06).cgColor
+
+        let searchPill = NSView()
+        searchPill.wantsLayer = true
+        searchPill.layer?.backgroundColor = NSColor.white.withAlphaComponent(0.05).cgColor
+        searchPill.layer?.cornerRadius = 9
+        let search = makeLabel("Search meetings, people (\u{2318}E)", size: 13, color: mutedText, weight: .regular, centered: false)
+
+        let profileChip = NSView()
+        profileChip.wantsLayer = true
+        profileChip.layer?.backgroundColor = accentBlue.withAlphaComponent(0.22).cgColor
+        profileChip.layer?.cornerRadius = 9
+        let name = makeLabel(profile?.name ?? "User", size: 13, color: primaryText, weight: .semibold, centered: false)
+
+        let welcome = makeLabel("Home", size: 15, color: secondaryText, weight: .medium, centered: false)
+        let timeTitle = makeLabel("--:--", size: 50, color: primaryText, weight: .bold, centered: true)
+        let dateTitle = makeLabel("-", size: 16, color: secondaryText, weight: .regular, centered: true)
 
         let actions = NSStackView(views: [
             makeActionTile(title: "New meeting", symbol: "video.fill", color: accentOrange),
             makeActionTile(title: "Join", symbol: "plus", color: accentBlue),
-            makeActionTile(title: "Schedule", symbol: "calendar", color: accentBlue),
+            makeActionTile(title: "Schedule", symbol: "calendar", color: accentBlue, action: #selector(scheduleMeetingWebTapped)),
             makeActionTile(title: "Share screen", symbol: "rectangle.portrait.and.arrow.forward", color: accentBlue),
             makeActionTile(title: "My notes", symbol: "pencil", color: accentBlue)
         ])
         actions.orientation = .horizontal
-        actions.spacing = 26
+        actions.spacing = 16
         actions.alignment = .centerY
         actions.distribution = .fillEqually
 
         let panel = NSView()
         panel.wantsLayer = true
         panel.layer?.backgroundColor = secondaryCardBackground.cgColor
-        panel.layer?.cornerRadius = 14
-
-        let panelHeader = makeLabel("Today, Apr 14", size: 32, color: primaryText, weight: .semibold, centered: true)
-        let meetingsStatus = makeLabel("Zoom meetings", size: 12, color: secondaryText, weight: .regular, centered: false)
-        let noMeeting = makeLabel("No meetings scheduled.", size: 32, color: secondaryText, weight: .regular, centered: true)
+        panel.layer?.cornerRadius = 16
+        panel.layer?.borderWidth = 1
+        panel.layer?.borderColor = NSColor.white.withAlphaComponent(0.07).cgColor
+
+        let todaysDateFormatter = DateFormatter()
+        todaysDateFormatter.dateFormat = "EEEE, MMM d"
+        let panelHeader = makeLabel(todaysDateFormatter.string(from: Date()), size: 22, color: primaryText, weight: .semibold, centered: false)
+        let meetingsStatus = makeLabel("Upcoming meetings", size: 12, color: secondaryText, weight: .medium, centered: false)
+        let noMeeting = makeLabel("No meetings scheduled for today.", size: 18, color: secondaryText, weight: .regular, centered: true)
         let meetingsScrollView = NSScrollView()
         meetingsScrollView.drawsBackground = false
         meetingsScrollView.hasVerticalScroller = true
@@ -574,15 +596,21 @@ class ViewController: NSViewController {
         meetingsStack.orientation = .vertical
         meetingsStack.spacing = 10
         meetingsStack.alignment = .leading
-        let openRecordings = makeLabel("Open recordings  ›", size: 30, color: secondaryText, weight: .regular, centered: false)
+        let openRecordings = NSButton(title: "Open recordings", target: nil, action: nil)
+        openRecordings.isBordered = false
+        openRecordings.font = .systemFont(ofSize: 14, weight: .semibold)
+        openRecordings.contentTintColor = primaryText
         openRecordings.wantsLayer = true
         openRecordings.layer?.backgroundColor = NSColor(calibratedRed: 31 / 255, green: 33 / 255, blue: 39 / 255, alpha: 1).cgColor
+        openRecordings.layer?.cornerRadius = 11
+        openRecordings.layer?.borderWidth = 1
+        openRecordings.layer?.borderColor = NSColor.white.withAlphaComponent(0.07).cgColor
 
         let contentColumn = NSView()
         contentColumn.translatesAutoresizingMaskIntoConstraints = false
         content.addSubview(contentColumn)
 
-        [topBar, search, name, timeTitle, dateTitle, actions, panel, panelHeader, meetingsStatus, noMeeting, meetingsScrollView, openRecordings].forEach {
+        [topBar, searchPill, search, profileChip, name, welcome, timeTitle, dateTitle, actions, panel, panelHeader, meetingsStatus, noMeeting, meetingsScrollView, openRecordings].forEach {
             $0.translatesAutoresizingMaskIntoConstraints = false
             contentColumn.addSubview($0)
         }
@@ -598,36 +626,52 @@ class ViewController: NSViewController {
             contentColumn.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -20),
 
             topBar.topAnchor.constraint(equalTo: contentColumn.topAnchor, constant: 12),
-            topBar.leadingAnchor.constraint(equalTo: contentColumn.leadingAnchor, constant: 20),
-            topBar.trailingAnchor.constraint(equalTo: contentColumn.trailingAnchor, constant: -80),
-            topBar.heightAnchor.constraint(equalToConstant: 36),
-
-            search.centerXAnchor.constraint(equalTo: topBar.centerXAnchor),
-            search.centerYAnchor.constraint(equalTo: topBar.centerYAnchor),
-
-            name.trailingAnchor.constraint(equalTo: contentColumn.trailingAnchor, constant: -8),
-            name.centerYAnchor.constraint(equalTo: topBar.centerYAnchor),
-
-            timeTitle.topAnchor.constraint(equalTo: topBar.bottomAnchor, constant: 62),
+            topBar.leadingAnchor.constraint(equalTo: contentColumn.leadingAnchor, constant: 12),
+            topBar.trailingAnchor.constraint(equalTo: contentColumn.trailingAnchor, constant: -12),
+            topBar.heightAnchor.constraint(equalToConstant: 50),
+
+            searchPill.leadingAnchor.constraint(equalTo: topBar.leadingAnchor, constant: 12),
+            searchPill.centerYAnchor.constraint(equalTo: topBar.centerYAnchor),
+            searchPill.heightAnchor.constraint(equalToConstant: 32),
+            searchPill.widthAnchor.constraint(equalToConstant: 320),
+            search.leadingAnchor.constraint(equalTo: searchPill.leadingAnchor, constant: 12),
+            search.trailingAnchor.constraint(equalTo: searchPill.trailingAnchor, constant: -12),
+            search.centerYAnchor.constraint(equalTo: searchPill.centerYAnchor),
+
+            profileChip.trailingAnchor.constraint(equalTo: topBar.trailingAnchor, constant: -10),
+            profileChip.centerYAnchor.constraint(equalTo: topBar.centerYAnchor),
+            profileChip.heightAnchor.constraint(equalToConstant: 32),
+            profileChip.widthAnchor.constraint(greaterThanOrEqualToConstant: 92),
+            name.leadingAnchor.constraint(equalTo: profileChip.leadingAnchor, constant: 12),
+            name.trailingAnchor.constraint(equalTo: profileChip.trailingAnchor, constant: -12),
+            name.centerYAnchor.constraint(equalTo: profileChip.centerYAnchor),
+
+            welcome.topAnchor.constraint(equalTo: topBar.bottomAnchor, constant: 18),
+            welcome.centerXAnchor.constraint(equalTo: contentColumn.centerXAnchor),
+
+            timeTitle.topAnchor.constraint(equalTo: welcome.bottomAnchor, constant: 14),
             timeTitle.centerXAnchor.constraint(equalTo: contentColumn.centerXAnchor),
-            dateTitle.topAnchor.constraint(equalTo: timeTitle.bottomAnchor, constant: 8),
+            dateTitle.topAnchor.constraint(equalTo: timeTitle.bottomAnchor, constant: 6),
             dateTitle.centerXAnchor.constraint(equalTo: contentColumn.centerXAnchor),
 
-            actions.topAnchor.constraint(equalTo: dateTitle.bottomAnchor, constant: 42),
+            actions.topAnchor.constraint(equalTo: dateTitle.bottomAnchor, constant: 30),
             actions.centerXAnchor.constraint(equalTo: contentColumn.centerXAnchor),
-            actions.widthAnchor.constraint(equalToConstant: 560),
+            actions.leadingAnchor.constraint(greaterThanOrEqualTo: contentColumn.leadingAnchor, constant: 18),
+            actions.trailingAnchor.constraint(lessThanOrEqualTo: contentColumn.trailingAnchor, constant: -18),
             actions.heightAnchor.constraint(equalToConstant: 96),
 
-            panel.topAnchor.constraint(equalTo: actions.bottomAnchor, constant: 32),
-            panel.centerXAnchor.constraint(equalTo: contentColumn.centerXAnchor),
-            panel.widthAnchor.constraint(equalToConstant: 640),
-            panel.heightAnchor.constraint(equalToConstant: 285),
+            panel.topAnchor.constraint(equalTo: actions.bottomAnchor, constant: 24),
+            panel.leadingAnchor.constraint(equalTo: contentColumn.leadingAnchor, constant: 28),
+            panel.trailingAnchor.constraint(equalTo: contentColumn.trailingAnchor, constant: -28),
+            panel.heightAnchor.constraint(equalToConstant: 300),
+            panel.bottomAnchor.constraint(lessThanOrEqualTo: contentColumn.bottomAnchor, constant: -20),
 
-            panelHeader.topAnchor.constraint(equalTo: panel.topAnchor, constant: 15),
-            panelHeader.centerXAnchor.constraint(equalTo: panel.centerXAnchor),
+            panelHeader.topAnchor.constraint(equalTo: panel.topAnchor, constant: 16),
+            panelHeader.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 18),
             meetingsStatus.centerYAnchor.constraint(equalTo: panelHeader.centerYAnchor),
-            meetingsStatus.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 18),
-            noMeeting.centerXAnchor.constraint(equalTo: panel.centerXAnchor),
+            meetingsStatus.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -18),
+            noMeeting.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 18),
+            noMeeting.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -18),
             noMeeting.centerYAnchor.constraint(equalTo: panel.centerYAnchor),
 
             meetingsScrollView.topAnchor.constraint(equalTo: panel.topAnchor, constant: 58),
@@ -641,10 +685,10 @@ class ViewController: NSViewController {
             meetingsStack.trailingAnchor.constraint(equalTo: meetingsDocument.trailingAnchor),
             meetingsStack.bottomAnchor.constraint(equalTo: meetingsDocument.bottomAnchor),
 
-            openRecordings.leadingAnchor.constraint(equalTo: panel.leadingAnchor),
-            openRecordings.trailingAnchor.constraint(equalTo: panel.trailingAnchor),
-            openRecordings.bottomAnchor.constraint(equalTo: panel.bottomAnchor),
-            openRecordings.heightAnchor.constraint(equalToConstant: 44)
+            openRecordings.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 14),
+            openRecordings.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -14),
+            openRecordings.bottomAnchor.constraint(equalTo: panel.bottomAnchor, constant: -12),
+            openRecordings.heightAnchor.constraint(equalToConstant: 38)
         ])
 
         timeLabel = timeTitle
@@ -722,17 +766,20 @@ class ViewController: NSViewController {
         return sidebar
     }
 
-    private func makeActionTile(title: String, symbol: String, color: NSColor) -> NSView {
+    private func makeActionTile(title: String, symbol: String, color: NSColor, action: Selector? = nil) -> NSView {
         let root = NSView()
         root.translatesAutoresizingMaskIntoConstraints = false
         root.widthAnchor.constraint(equalToConstant: 100).isActive = true
         root.heightAnchor.constraint(equalToConstant: 96).isActive = true
 
-        let iconButton = NSButton()
+        let iconButton = NSButton(title: "", target: action == nil ? nil : self, action: action)
         iconButton.isBordered = false
         iconButton.wantsLayer = true
         iconButton.layer?.backgroundColor = color.cgColor
         iconButton.layer?.cornerRadius = 18
+        iconButton.layer?.shadowOpacity = 0.2
+        iconButton.layer?.shadowRadius = 7
+        iconButton.layer?.shadowOffset = NSSize(width: 0, height: -1)
         iconButton.image = NSImage(systemSymbolName: symbol, accessibilityDescription: title)
         iconButton.contentTintColor = .white
         iconButton.imageScaling = .scaleProportionallyUpOrDown