|
|
@@ -32,12 +32,9 @@ final class DashboardView: NSView {
|
|
|
private let mainOverlay = NSStackView()
|
|
|
private let greetingLabel = NSTextField(labelWithString: "")
|
|
|
private let subtitleLabel = NSTextField(labelWithString: "")
|
|
|
- private let insightsCard = NSView()
|
|
|
- private let insightsTitleLabel = NSTextField(labelWithString: "")
|
|
|
- private let insightsBodyLabel = NSTextField(labelWithString: "")
|
|
|
- private let togglesLabel = NSTextField(labelWithString: "Show:")
|
|
|
- private let savedToggleButton = NSButton(title: "Saved", target: nil, action: nil)
|
|
|
- private let interviewsToggleButton = NSButton(title: "Interviews", target: nil, action: nil)
|
|
|
+ private let searchCard = NSView()
|
|
|
+ private let searchIcon = NSImageView()
|
|
|
+ private let searchField = NSTextField()
|
|
|
private let sparkleView = NSImageView()
|
|
|
private let scrollView = NSScrollView()
|
|
|
|
|
|
@@ -59,8 +56,6 @@ final class DashboardView: NSView {
|
|
|
func render(_ data: DashboardData) {
|
|
|
greetingLabel.stringValue = "Welcome"
|
|
|
subtitleLabel.stringValue = data.subtitle
|
|
|
- insightsTitleLabel.stringValue = data.profileInsightsTitle
|
|
|
- insightsBodyLabel.stringValue = data.profileInsightsBody
|
|
|
configureSidebar(data.sidebarItems)
|
|
|
updateDocumentLayout()
|
|
|
}
|
|
|
@@ -130,7 +125,7 @@ final class DashboardView: NSView {
|
|
|
topInset.translatesAutoresizingMaskIntoConstraints = false
|
|
|
topInset.heightAnchor.constraint(equalToConstant: 32).isActive = true
|
|
|
|
|
|
- configureInsightsCard()
|
|
|
+ configureSearchCard()
|
|
|
|
|
|
let titleBlock = NSStackView(views: [greetingLabel, subtitleLabel])
|
|
|
titleBlock.orientation = .vertical
|
|
|
@@ -149,7 +144,7 @@ final class DashboardView: NSView {
|
|
|
mainOverlay.addArrangedSubview(topInset)
|
|
|
mainOverlay.addArrangedSubview(titleBlock)
|
|
|
mainOverlay.addArrangedSubview(midSpacer)
|
|
|
- mainOverlay.addArrangedSubview(insightsCard)
|
|
|
+ mainOverlay.addArrangedSubview(searchCard)
|
|
|
mainOverlay.addArrangedSubview(overlayBottomSpacer)
|
|
|
|
|
|
sparkleView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
@@ -185,6 +180,8 @@ final class DashboardView: NSView {
|
|
|
mainOverlay.topAnchor.constraint(equalTo: mainHost.topAnchor),
|
|
|
mainOverlay.bottomAnchor.constraint(equalTo: mainHost.bottomAnchor, constant: -24),
|
|
|
|
|
|
+ searchCard.widthAnchor.constraint(equalTo: mainOverlay.widthAnchor, multiplier: 0.82),
|
|
|
+
|
|
|
greetingLabel.leadingAnchor.constraint(equalTo: mainOverlay.leadingAnchor, constant: 24),
|
|
|
greetingLabel.trailingAnchor.constraint(equalTo: mainOverlay.trailingAnchor, constant: -24),
|
|
|
subtitleLabel.leadingAnchor.constraint(equalTo: greetingLabel.leadingAnchor),
|
|
|
@@ -195,70 +192,58 @@ final class DashboardView: NSView {
|
|
|
])
|
|
|
}
|
|
|
|
|
|
- private func configureInsightsCard() {
|
|
|
- insightsCard.wantsLayer = true
|
|
|
- insightsCard.layer?.backgroundColor = Theme.cardBackground.cgColor
|
|
|
- insightsCard.layer?.cornerRadius = 18
|
|
|
- insightsCard.translatesAutoresizingMaskIntoConstraints = false
|
|
|
-
|
|
|
- insightsTitleLabel.font = .systemFont(ofSize: 20, weight: .semibold)
|
|
|
- insightsTitleLabel.textColor = Theme.primaryText
|
|
|
- insightsTitleLabel.alignment = .center
|
|
|
- insightsTitleLabel.maximumNumberOfLines = 1
|
|
|
-
|
|
|
- insightsBodyLabel.font = .systemFont(ofSize: 13, weight: .regular)
|
|
|
- insightsBodyLabel.textColor = Theme.secondaryText
|
|
|
- insightsBodyLabel.alignment = .center
|
|
|
- insightsBodyLabel.maximumNumberOfLines = 4
|
|
|
- insightsBodyLabel.lineBreakMode = .byWordWrapping
|
|
|
- insightsBodyLabel.preferredMaxLayoutWidth = 400
|
|
|
-
|
|
|
- togglesLabel.font = .systemFont(ofSize: 12, weight: .medium)
|
|
|
- togglesLabel.textColor = Theme.tertiaryText
|
|
|
- togglesLabel.alignment = .center
|
|
|
- togglesLabel.maximumNumberOfLines = 1
|
|
|
-
|
|
|
- styleToggle(savedToggleButton)
|
|
|
- styleToggle(interviewsToggleButton)
|
|
|
-
|
|
|
- let toggleRow = NSStackView(views: [savedToggleButton, interviewsToggleButton])
|
|
|
- toggleRow.orientation = .horizontal
|
|
|
- toggleRow.spacing = 10
|
|
|
- toggleRow.alignment = .centerY
|
|
|
-
|
|
|
- let inner = NSStackView(views: [
|
|
|
- insightsTitleLabel,
|
|
|
- insightsBodyLabel,
|
|
|
- togglesLabel,
|
|
|
- toggleRow
|
|
|
- ])
|
|
|
- inner.orientation = .vertical
|
|
|
- inner.spacing = 10
|
|
|
- inner.alignment = .centerX
|
|
|
- inner.distribution = .fill
|
|
|
- inner.translatesAutoresizingMaskIntoConstraints = false
|
|
|
+ private func configureSearchCard() {
|
|
|
+ searchCard.wantsLayer = true
|
|
|
+ searchCard.layer?.backgroundColor = Theme.cardBackground.cgColor
|
|
|
+ searchCard.layer?.cornerRadius = 14
|
|
|
+ searchCard.layer?.borderWidth = 1
|
|
|
+ searchCard.layer?.borderColor = NSColor(calibratedWhite: 1, alpha: 0.06).cgColor
|
|
|
+ searchCard.translatesAutoresizingMaskIntoConstraints = false
|
|
|
+ searchCard.setContentHuggingPriority(.defaultHigh, for: .vertical)
|
|
|
+
|
|
|
+ searchIcon.translatesAutoresizingMaskIntoConstraints = false
|
|
|
+ searchIcon.symbolConfiguration = NSImage.SymbolConfiguration(pointSize: 15, weight: .medium)
|
|
|
+ searchIcon.image = NSImage(systemSymbolName: "magnifyingglass", accessibilityDescription: "Search")
|
|
|
+ searchIcon.contentTintColor = Theme.secondaryText
|
|
|
+
|
|
|
+ searchField.translatesAutoresizingMaskIntoConstraints = false
|
|
|
+ searchField.isBordered = false
|
|
|
+ searchField.drawsBackground = false
|
|
|
+ searchField.focusRingType = .none
|
|
|
+ searchField.font = .systemFont(ofSize: 14, weight: .regular)
|
|
|
+ searchField.textColor = Theme.primaryText
|
|
|
+ searchField.placeholderAttributedString = NSAttributedString(
|
|
|
+ string: "Search jobs, companies, or locations",
|
|
|
+ attributes: [
|
|
|
+ .foregroundColor: Theme.tertiaryText,
|
|
|
+ .font: NSFont.systemFont(ofSize: 14, weight: .regular)
|
|
|
+ ]
|
|
|
+ )
|
|
|
+ searchField.cell?.usesSingleLineMode = true
|
|
|
+ searchField.cell?.wraps = false
|
|
|
+ searchField.cell?.isScrollable = true
|
|
|
+ searchField.target = self
|
|
|
+ searchField.action = #selector(didSubmitSearch)
|
|
|
+
|
|
|
+ searchCard.addSubview(searchIcon)
|
|
|
+ searchCard.addSubview(searchField)
|
|
|
|
|
|
- insightsCard.setContentHuggingPriority(.defaultHigh, for: .vertical)
|
|
|
- insightsCard.addSubview(inner)
|
|
|
NSLayoutConstraint.activate([
|
|
|
- inner.leadingAnchor.constraint(equalTo: insightsCard.leadingAnchor, constant: 32),
|
|
|
- inner.trailingAnchor.constraint(equalTo: insightsCard.trailingAnchor, constant: -32),
|
|
|
- inner.topAnchor.constraint(equalTo: insightsCard.topAnchor, constant: 22),
|
|
|
- inner.bottomAnchor.constraint(equalTo: insightsCard.bottomAnchor, constant: -22),
|
|
|
- insightsCard.widthAnchor.constraint(equalToConstant: 440)
|
|
|
+ searchCard.heightAnchor.constraint(equalToConstant: 48),
|
|
|
+
|
|
|
+ searchIcon.leadingAnchor.constraint(equalTo: searchCard.leadingAnchor, constant: 16),
|
|
|
+ searchIcon.centerYAnchor.constraint(equalTo: searchCard.centerYAnchor),
|
|
|
+ searchIcon.widthAnchor.constraint(equalToConstant: 18),
|
|
|
+ searchIcon.heightAnchor.constraint(equalToConstant: 18),
|
|
|
+
|
|
|
+ searchField.leadingAnchor.constraint(equalTo: searchIcon.trailingAnchor, constant: 10),
|
|
|
+ searchField.trailingAnchor.constraint(equalTo: searchCard.trailingAnchor, constant: -16),
|
|
|
+ searchField.centerYAnchor.constraint(equalTo: searchCard.centerYAnchor)
|
|
|
])
|
|
|
}
|
|
|
|
|
|
- private func styleToggle(_ button: NSButton) {
|
|
|
- button.bezelStyle = .rounded
|
|
|
- button.font = .systemFont(ofSize: 12, weight: .medium)
|
|
|
- button.contentTintColor = Theme.secondaryText
|
|
|
- button.wantsLayer = true
|
|
|
- button.layer?.backgroundColor = Theme.toggleBackground.cgColor
|
|
|
- button.layer?.cornerRadius = 8
|
|
|
- button.translatesAutoresizingMaskIntoConstraints = false
|
|
|
- button.widthAnchor.constraint(equalToConstant: 108).isActive = true
|
|
|
- button.heightAnchor.constraint(equalToConstant: 30).isActive = true
|
|
|
+ @objc private func didSubmitSearch() {
|
|
|
+ // Hook up search submission here when wiring up real data.
|
|
|
}
|
|
|
|
|
|
private func configureSidebar(_ items: [SidebarItem]) {
|